Skip to content

Commit 7134a4a

Browse files
thrawnydavidism
authored andcommitted
Convert tests to pytest
1 parent ecb131b commit 7134a4a

15 files changed

+725
-814
lines changed

test_sqlalchemy.py

Lines changed: 0 additions & 814 deletions
This file was deleted.

tests/conftest.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from datetime import datetime
2+
3+
import flask
4+
import pytest
5+
6+
import flask_sqlalchemy as fsa
7+
8+
9+
@pytest.fixture
10+
def app(request):
11+
app = flask.Flask(request.module.__name__)
12+
app.testing = True
13+
return app
14+
15+
16+
@pytest.fixture
17+
def db(app):
18+
return fsa.SQLAlchemy(app)
19+
20+
21+
@pytest.fixture
22+
def Todo(db):
23+
class Todo(db.Model):
24+
__tablename__ = 'todos'
25+
id = db.Column('todo_id', db.Integer, primary_key=True)
26+
title = db.Column(db.String(60))
27+
text = db.Column(db.String)
28+
done = db.Column(db.Boolean)
29+
pub_date = db.Column(db.DateTime)
30+
31+
def __init__(self, title, text):
32+
self.title = title
33+
self.text = text
34+
self.done = False
35+
self.pub_date = datetime.utcnow()
36+
db.create_all()
37+
yield Todo
38+
db.drop_all()

tests/test_basic_app.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import flask
2+
3+
import flask_sqlalchemy as fsa
4+
5+
6+
def test_basic_insert(app, db, Todo):
7+
@app.route('/')
8+
def index():
9+
return '\n'.join(x.title for x in Todo.query.all())
10+
11+
@app.route('/add', methods=['POST'])
12+
def add():
13+
form = flask.request.form
14+
todo = Todo(form['title'], form['text'])
15+
db.session.add(todo)
16+
db.session.commit()
17+
return 'added'
18+
19+
c = app.test_client()
20+
c.post('/add', data=dict(title='First Item', text='The text'))
21+
c.post('/add', data=dict(title='2nd Item', text='The text'))
22+
rv = c.get('/')
23+
assert rv.data == b'First Item\n2nd Item'
24+
25+
26+
def test_query_recording(app, db, Todo):
27+
with app.test_request_context():
28+
todo = Todo('Test 1', 'test')
29+
db.session.add(todo)
30+
db.session.flush()
31+
todo.done = True
32+
db.session.commit()
33+
34+
queries = fsa.get_debug_queries()
35+
assert len(queries) == 2
36+
37+
query = queries[0]
38+
assert 'insert into' in query.statement.lower()
39+
assert query.parameters[0] == 'Test 1'
40+
assert query.parameters[1] == 'test'
41+
assert 'test_basic_app.py' in query.context
42+
assert 'test_query_recording' in query.context
43+
44+
query = queries[1]
45+
assert 'update' in query.statement.lower()
46+
assert query.parameters[0] == 1
47+
assert query.parameters[1] == 1
48+
49+
50+
def test_helper_api(db):
51+
assert db.metadata == db.Model.metadata

tests/test_binds.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
def test_basic_binds(app, db):
3+
app.config['SQLALCHEMY_BINDS'] = {
4+
'foo': 'sqlite://',
5+
'bar': 'sqlite://'
6+
}
7+
8+
class Foo(db.Model):
9+
__bind_key__ = 'foo'
10+
__table_args__ = {"info": {"bind_key": "foo"}}
11+
id = db.Column(db.Integer, primary_key=True)
12+
13+
class Bar(db.Model):
14+
__bind_key__ = 'bar'
15+
id = db.Column(db.Integer, primary_key=True)
16+
17+
class Baz(db.Model):
18+
id = db.Column(db.Integer, primary_key=True)
19+
20+
db.create_all()
21+
22+
# simple way to check if the engines are looked up properly
23+
assert db.get_engine(app, None) == db.engine
24+
for key in 'foo', 'bar':
25+
engine = db.get_engine(app, key)
26+
connector = app.extensions['sqlalchemy'].connectors[key]
27+
assert engine == connector.get_engine()
28+
assert str(engine.url) == app.config['SQLALCHEMY_BINDS'][key]
29+
30+
# do the models have the correct engines?
31+
assert db.metadata.tables['foo'].info['bind_key'] == 'foo'
32+
assert db.metadata.tables['bar'].info['bind_key'] == 'bar'
33+
assert db.metadata.tables['baz'].info.get('bind_key') is None
34+
35+
# see the tables created in an engine
36+
metadata = db.MetaData()
37+
metadata.reflect(bind=db.get_engine(app, 'foo'))
38+
assert len(metadata.tables) == 1
39+
assert 'foo' in metadata.tables
40+
41+
metadata = db.MetaData()
42+
metadata.reflect(bind=db.get_engine(app, 'bar'))
43+
assert len(metadata.tables) == 1
44+
assert 'bar' in metadata.tables
45+
46+
metadata = db.MetaData()
47+
metadata.reflect(bind=db.get_engine(app))
48+
assert len(metadata.tables) == 1
49+
assert 'baz' in metadata.tables
50+
51+
# do the session have the right binds set?
52+
assert db.get_binds(app) == {
53+
Foo.__table__: db.get_engine(app, 'foo'),
54+
Bar.__table__: db.get_engine(app, 'bar'),
55+
Baz.__table__: db.get_engine(app, None)
56+
}
57+
58+
59+
def test_abstract_binds(app, db):
60+
app.config['SQLALCHEMY_BINDS'] = {
61+
'foo': 'sqlite://'
62+
}
63+
64+
class AbstractFooBoundModel(db.Model):
65+
__abstract__ = True
66+
__bind_key__ = 'foo'
67+
68+
class FooBoundModel(AbstractFooBoundModel):
69+
id = db.Column(db.Integer, primary_key=True)
70+
71+
db.create_all()
72+
73+
# does the model have the correct engines?
74+
assert db.metadata.tables['foo_bound_model'].info['bind_key'] == 'foo'
75+
76+
# see the tables created in an engine
77+
metadata = db.MetaData()
78+
metadata.reflect(bind=db.get_engine(app, 'foo'))
79+
assert len(metadata.tables) == 1
80+
assert 'foo_bound_model' in metadata.tables

tests/test_commit_on_teardown.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import flask
2+
import pytest
3+
4+
5+
@pytest.fixture
6+
def client(app, db, Todo):
7+
app.testing = False
8+
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
9+
10+
@app.route('/')
11+
def index():
12+
return '\n'.join(x.title for x in Todo.query.all())
13+
14+
@app.route('/create', methods=['POST'])
15+
def create():
16+
db.session.add(Todo('Test one', 'test'))
17+
if flask.request.form.get('fail'):
18+
raise RuntimeError("Failing as requested")
19+
return 'ok'
20+
21+
return app.test_client()
22+
23+
24+
def test_commit_on_success(client):
25+
resp = client.post('/create')
26+
assert resp.status_code == 200
27+
assert client.get('/').data == b'Test one'
28+
29+
30+
def test_roll_back_on_failure(client):
31+
resp = client.post('/create', data={'fail': 'on'})
32+
assert resp.status_code == 500
33+
assert client.get('/').data == b''

tests/test_meta_data.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import sqlalchemy as sa
2+
3+
import flask_sqlalchemy as fsa
4+
5+
6+
def test_default_metadata(app):
7+
db = fsa.SQLAlchemy(app, metadata=None)
8+
9+
class One(db.Model):
10+
id = db.Column(db.Integer, primary_key=True)
11+
myindex = db.Column(db.Integer, index=True)
12+
13+
class Two(db.Model):
14+
id = db.Column(db.Integer, primary_key=True)
15+
one_id = db.Column(db.Integer, db.ForeignKey(One.id))
16+
myunique = db.Column(db.Integer, unique=True)
17+
18+
assert One.metadata.__class__ is sa.MetaData
19+
assert Two.metadata.__class__ is sa.MetaData
20+
21+
assert One.__table__.schema is None
22+
assert Two.__table__.schema is None
23+
24+
25+
def test_custom_metadata(app):
26+
class CustomMetaData(sa.MetaData):
27+
pass
28+
29+
custom_metadata = CustomMetaData(schema="test_schema")
30+
db = fsa.SQLAlchemy(app, metadata=custom_metadata)
31+
32+
class One(db.Model):
33+
id = db.Column(db.Integer, primary_key=True)
34+
myindex = db.Column(db.Integer, index=True)
35+
36+
class Two(db.Model):
37+
id = db.Column(db.Integer, primary_key=True)
38+
one_id = db.Column(db.Integer, db.ForeignKey(One.id))
39+
myunique = db.Column(db.Integer, unique=True)
40+
41+
assert One.metadata is custom_metadata
42+
assert Two.metadata is custom_metadata
43+
44+
assert One.metadata.__class__ is not sa.MetaData
45+
assert One.metadata.__class__ is CustomMetaData
46+
47+
assert Two.metadata.__class__ is not sa.MetaData
48+
assert Two.metadata.__class__ is CustomMetaData
49+
50+
assert One.__table__.schema == "test_schema"
51+
assert Two.__table__.schema == "test_schema"

tests/test_model_class.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import flask_sqlalchemy as fsa
2+
3+
4+
def test_custom_query_class(app):
5+
class CustomModelClass(fsa.Model):
6+
pass
7+
8+
db = fsa.SQLAlchemy(app, model_class=CustomModelClass)
9+
10+
class SomeModel(db.Model):
11+
id = db.Column(db.Integer, primary_key=True)
12+
13+
assert isinstance(SomeModel(), CustomModelClass)

tests/test_pagination.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import flask_sqlalchemy as fsa
2+
3+
4+
def test_basic_pagination():
5+
p = fsa.Pagination(None, 1, 20, 500, [])
6+
assert p.page == 1
7+
assert not p.has_prev
8+
assert p.has_next
9+
assert p.total == 500
10+
assert p.pages == 25
11+
assert p.next_num == 2
12+
assert list(p.iter_pages()) == [1, 2, 3, 4, 5, None, 24, 25]
13+
p.page = 10
14+
assert list(p.iter_pages()) == [1, 2, None, 8, 9, 10, 11, 12, 13, 14, None, 24, 25]
15+
16+
17+
def test_pagination_pages_when_0_items_per_page():
18+
p = fsa.Pagination(None, 1, 0, 500, [])
19+
assert p.pages == 0
20+
21+
22+
def test_query_paginate(app, db, Todo):
23+
with app.app_context():
24+
db.session.add_all([Todo('', '') for _ in range(100)])
25+
db.session.commit()
26+
27+
@app.route('/')
28+
def index():
29+
p = Todo.query.paginate()
30+
return '{0} items retrieved'.format(len(p.items))
31+
32+
c = app.test_client()
33+
# request default
34+
r = c.get('/')
35+
assert r.status_code == 200
36+
# request args
37+
r = c.get('/?per_page=10')
38+
assert r.data.decode('utf8') == '10 items retrieved'
39+
40+
with app.app_context():
41+
# query default
42+
p = Todo.query.paginate()
43+
assert p.total == 100

tests/test_query_class.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import flask_sqlalchemy as fsa
2+
3+
4+
def test_default_query_class(db):
5+
class Parent(db.Model):
6+
id = db.Column(db.Integer, primary_key=True)
7+
children = db.relationship("Child", backref="parent", lazy='dynamic')
8+
9+
class Child(db.Model):
10+
id = db.Column(db.Integer, primary_key=True)
11+
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))
12+
13+
p = Parent()
14+
c = Child()
15+
c.parent = p
16+
17+
assert type(Parent.query) == fsa.BaseQuery
18+
assert type(Child.query) == fsa.BaseQuery
19+
assert isinstance(p.children, fsa.BaseQuery)
20+
assert isinstance(db.session.query(Parent), fsa.BaseQuery)
21+
22+
23+
def test_custom_query_class(app):
24+
class CustomQueryClass(fsa.BaseQuery):
25+
pass
26+
27+
db = fsa.SQLAlchemy(app, query_class=CustomQueryClass)
28+
29+
class Parent(db.Model):
30+
id = db.Column(db.Integer, primary_key=True)
31+
children = db.relationship("Child", backref="parent", lazy='dynamic')
32+
33+
class Child(db.Model):
34+
id = db.Column(db.Integer, primary_key=True)
35+
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))
36+
37+
p = Parent()
38+
c = Child()
39+
c.parent = p
40+
41+
assert type(Parent.query) == CustomQueryClass
42+
assert type(Child.query) == CustomQueryClass
43+
assert isinstance(p.children, CustomQueryClass)
44+
assert db.Query == CustomQueryClass
45+
assert db.Model.query_class == CustomQueryClass
46+
assert isinstance(db.session.query(Parent), CustomQueryClass)
47+
48+
49+
def test_dont_override_model_default(app):
50+
class CustomQueryClass(fsa.BaseQuery):
51+
pass
52+
53+
db = fsa.SQLAlchemy(app, query_class=CustomQueryClass)
54+
55+
class SomeModel(db.Model):
56+
id = db.Column(db.Integer, primary_key=True)
57+
query_class = fsa.BaseQuery
58+
59+
assert type(SomeModel.query) == fsa.BaseQuery

0 commit comments

Comments
 (0)