unit testing - Python SQLAlchemy - Mocking a model attribute's "desc" method -
in application, there class each model holds commonly used queries (i guess it's of "repository" in ddd language). each of these classes passed sqlalchemy session object create queries upon construction. i'm having little difficulty in figuring best way assert queries being run in unit tests. using ubiquitous blog example, let's have "post" model columns , attributes "date" , "content". have "postrepository" method "find_latest" supposed query posts in descending order "date". looks like:
from myapp.models import post class postrepository(object): def __init__(self, session): self._s = session def find_latest(self): return self._s.query(post).order_by(post.date.desc())
i'm having trouble mocking post.date.desc() call. right i'm monkey patching mock in post.date.desc in unit test, feel there better approach.
edit: i'm using mox mock objects, current unit test looks like:
import unittest import mox class testpostrepository(unittest.testcase): def setup(self): self._mox = mox.mox() def _create_session_mock(self): sqlalchemy.orm.session import session return self._mox.createmock(session) def _create_query_mock(self): sqlalchemy.orm.query import query return self._mox.createmock(query) def _create_desc_mock(self): myapp.models import post return self._mox.createmock(post.date.desc) def test_find_latest(self): myapp.models.repositories import postrepository myapp.models import post expected_result = 'test' session_mock = self._create_session_mock() query_mock = self._create_query_mock() desc_mock = self._create_desc_mock() # monkey patch tmp = post.date.desc post.date.desc = desc_mock session_mock.query(post).andreturn(query_mock) query_mock.order_by(post.date.desc().andreturn('test')).andreturn(query_mock) query_mock.offset(0).andreturn(query_mock) query_mock.limit(10).andreturn(expected_result) self._mox.replayall() r = postrepository(session_mock) result = r.find_latest() self._mox.verifyall() self.assertequals(expected_result, result) post.date.desc = tmp
this work, though feels ugly , i'm not sure why fails without "andreturn('test')" piece of "post.date.desc().andreturn('test')"
i don't think you're gaining benefit using mocks testing queries. testing should testing logic of code, not implementation. better solution create fresh database, add objects it, run query on database, , determine if you're getting correct results back. example:
# create engine. starts fresh database engine = create_engine('sqlite://') # fills database tables needed. # if use declarative, metadata tables can found using base.metadata metadata.create_all(engine) # create session database session = sessionmaker(bind=engine)() # create posts using session , commit them ... # test repository object... repo = postrepository(session) results = repo.find_latest() # run assertions of results ...
now, you're testing logic of code. means can change implementation of method, long query works correctly, tests should still pass. if want, write method query gets objects, slices resulting list. test pass, should. later on, change implementation run query using sa expression apis, , test pass.
one thing keep in mind might have problems sqlite behaving differently database type. using sqlite in-memory gives fast tests, if want serious these tests, you'll want run them against same type of database you'll using in production well.
Comments
Post a Comment