An Introduction to Test::MockDBI
by Mark Leighton FisherJuly 21, 2005
Prelude
- Without having to modify your current program code or environment settings?
- Without having to set up multiple test databases?
- Without separating your test data from your test code?
- With tests for every bizarre value your program will ever have to face?
- With complete control over all database return values, along with all DBI method return values?
- With an easy, regex-based rules interface?
You test with Test::MockDBI, that's how. Test::MockDBI provides all of this by using Test::MockObject::Extends to mock up the entire DBI API. Without a solution like Test::MockDBI--a solution that enables direct manipulation of the DBI--you'll have to trace DBI methods through a series of test databases.
You can make test databases work, but:
- You'll need multiple (perhaps many) databases when you need multiple sets of mutually inconsistent values for complete test coverage.
- Some DBI failure modes are impossible to generate through any test database.
- Depending on the database toolset available, it may be difficult to insert all necessary test values--for example, Unicode values in ASCII applications, or bizarre file types in a document-manager application.
- Test databases, by definition, are separate from their corresponding test code. This increases the chance that the test code and the test data will fall out of sync with each other.
Using Test::MockDBI avoids these problems. Read on to learn how Test::MockDBI eases the job of testing DBI applications.
|
Related Reading
|
A Mock Up of the Entire DBI
Test::MockDBI mocks up the entire DBI API by using Test::MockObject::Extends to substitute a Test::MockObject::Extends object in place of the DBI. A feature of this approach is that if the DBI API changes (and you use that change), you will notice during testing if you haven't upgraded Test::MockDBI, as your program will complain about missing DBI API method(s).
Mocking up the entire DBI means that you can add the DBI testing code into an existing application without changing the initial application code--using Test::MockDBI is entirely transparent to the rest of your application, as it neither knows nor cares that it's using Test::MockDBI in place of the DBI. This property of transparency is what drove me to develop Test::MockDBI, as it meant I could add the Test::MockDBI DBI testing code to existing client applications without modifying the existing code (handy, for us consultants).
Further enhancing Test::MockDBI's transparency is the DBI testing type
class value. Testing is only enabled when the DBI testing type is non-zero, so
you can just leave the DBI testing code additions in your production code--users will not even know about your DBI testing code unless you tell them.
Mocking up the entire DBI also means that you have complete control of the
DBI's behavior during testing. Often, you can simulate a SELECT
DBI transaction with a simple state machine that returns just a few rows from
the (mocked up) database. Test::MockDBI lets you use a CODEREF to supply
database return values, so you can easily put a simple state machine into the
CODEREF to supply the necessary database values for testing. You could even put
a delay loop into the CODEREF when you need to perform speed tests on your
code.
Rules-Based DBI Testing
You control the mocked-up DBI of Test::MockDBI with one or more rules that
you insert as Test::MockDBI method calls into your program. The default DBI
method values provided by Test::MockDBI make the database appear to have a hole
in the bottom of it--all method calls return OK, but you can't get any data
out of the database. Rules for DBI methods that return database values (the
fetch*() and select*() methods) can use either a
value that they return directly for matching method calls, or a CODEREF called
to provide a value each time that rule fires. A rule matches when its DBI
testing type is the current testing type and the current SQL matches the rule's
regular expression. Rules fire in the order in which you declare them, so
usually you want to order your rules from most-specific to least-specific.
The DBI testing type is an unsigned integer matching /^d+$/.
When the DBI testing type is zero, there will be no DBI testing (or at least, no
mocked-up DBI testing) performed, and the program will use the DBI normally. A
zero DBI testing type value in a rule means the rule could fire for any
non-zero DBI testing type value--that is, zero is the wildcard DBI testing
type value for rules. Set the DBI testing type either by a first command-line
argument of the form:
--dbitest[=DTT]
where the optional DTT is the DBI testing type (defaulting to
one), or through Test::MockDBI's set_dbi_test_type() method.
Setting the DBI testing type through a first command-line argument has the
advantage of requiring no modifications to the code under test, as this
command-line processing is done so early (during BEGIN time for
Test::MockDBI) that the code under test should be ignorant of whether this
processing ever happened.

