In previous chapters, we have written automated tests that involved stubbing out the data access layer. Now that we are in the heart of writing the data access layer, we need to look at ways to test our queries. To be specific, we need to make sure we are using DatabaseTemplate
correctly, along with any custom row mapper we write.
Mocks are used to primarily record what functions are called, and provide options of returning certain values. The idea is to create a set of expected actions, and then call the actual API and measure if this is what happened. This is compared to stubs, which you code yourself and provide canned answers and don't necessarily care what methods were called. Both of these tools are useful for automated testing.
Spring Python uses pmock, a Python library inspired by the fluent API of jMock (http://www.jmock.org/), to do some of its automated testing. You don't have to use this particular mocking library. There are lots of other candidates around. For our purposes we are going to use it here to show the general idea of mocking your data access layer for testing.
Due to lack of updates from the original developers of pmock
, the source code of this library was added to Spring Python's set of managed code, and has some custom updates. See http://springpython.webfactional.com for details on downloading.
DataAccess
class that uses DatabaseTemplate
to fetch the number of articles we have.class DataAccess(object): def __init__(self, conn_factory): self.dt = DatabaseTemplate(conn_factory) def count_wiki_articles(self): return self.dt.query_for_object("SELECT COUNT(*) FROM ARTICLE")
This simple data access layer has one method: count_wiki_articles
. It utilizes the code we wrote earlier involving the DatabaseTemplate
. In this example, DataAccess
expects to be initialized with a connection factory.
Now, to test this out, we need DatabaseTemplate
to do its job, but we want to catch it at the right point in order to inject some pre-built values. The piece of code that does the heavy lifting is Spring Python's cursor object, which is supplied by a connection. This means we need to code a special stubbed out connection factory that will hold a mocked cursor.
class MockedConnection(object): def __init__(self): self.mockCursor = None def cursor(self): return self.mockCursor
This connection will supply DatabaseTemplate
with a special type of mocked cursor. Further down, we will see how mockCursor
gets populated.
DatabaseTemplate
that produces this type of connection.class MockingDBFactory(ConnectionFactory): def __init__(self): ConnectionFactory.__init__(self, [types.TupleType]) self.mockedConnection = MockedConnection() def connect(self): return self.mockedConnection
When this connection factory is asked to connect to the database, it will return a MockedConnection
.
MockTestCase
. This is a special type of unit test that provides extra hooks to library calls of pmock
.from pmock import * class DataAccessMockTestCase(MockTestCase): def setUp(self): # Create a mock instance to record events self.mock = self.mock() conn_factory = MockingDBFactory() conn_factory.mockedConnection.mockCursor = self.mock self.data_access = DataAccess(conn_factory)
Here in the setUp
method for our test, we grab an instance of mock()
. This object has APIs to record expectations. The object is meant to be injected so that function calls are then made against it, and at the end of a MockTestCase
test method, the results are compared with the expectations.
In this situation, the mockCursor
is the key holder of the mock. There is also a local copy, so that the MockTestCase
has a handle to check out the results.
def testCountingArticles(self): self.mock.expects(once()).method("execute") self.mock.expects(once()).method("fetchall") .will(return_value([(2,)])) count = self.data_access.count_wiki_articles() self.assertEquals(count, 2)
The first two steps of this test use the mock object to defi ne expectations. The mock is expected to receive an execute method call once, and also a fetchall method call. The fetchall will return a value of [(2,)].
Not only do we get to check the assertions, but pmock will verify that each of these methods was invoked by DatabaseTemplate.
The first two steps of this test use the mock object to define expectations. The mock
is expected to receive an execute
method call once, and also a fetchall
method call. The fetchall
will return a value of [(2,)]
.
Not only do we get to check the assertions, but pmock
will verify that each of these methods was invoked by DatabaseTemplate
.