|
One of the main advantages of
DataConstructor
is that it lets you drive your database from tests. The feature that makes TDD database
development so effective with
DataConstructor
is versioning. The ability to upgrade a database to an arbitrary
version gives you a lot of visibility into the upgrade process itself.
Click here
to get started on your free 30-day trial.
We have found that it is best to write three kinds of tests:
- Acceptance tests
- Unit tests
- Transition tests
You don't have to write a new test in each category for each version. Sometimes
you will leave the acceptance tests alone and focus on transition and unit tests.
Sometimes you will be writing new acceptance tests and not feel the need to test
a transition.
Acceptance Tests
Acceptance tests prove what your database does. It's no different
from traditional software development. In fact, most users don't write acceptance
tests specifically for the database. Instead, they integrate
DataConstructor
into their FIT fixtures. Doing so will give you certainty:
you'll know that the upgrade path applied to production has been tested to death
by your automated builds. Because requirements change, acceptance tests can
change.
Acceptance tests that touch the database should always target the "current" version
of the database. It's up to you to define what "current" means for your team
but a good rule of thumb would be "whatever version you're going to release, next."
If you integrate
DataConstructor
directly into your FIT fixtures, then you'll have a really good
idea of which version is "current:" it's the version that makes all of your tests
pass. Testing the "current" version of your database gives you certainty
in what your database does, now.
If you write acceptance tests just for your database we've found it's best to route
them through at least one layer of indirection. For instance, acceptance testing
your database through a data object persistence layer is probably going to work
better than doing so with direct SQL queries.
Good acceptance tests should prove that your software is doing its job. For
instance "I can't add an Order Line Item without an Inventory Item Type," is an
acceptance test. By contrast, a test proving the existence of a foreign key
relationship between OrderLineItems and InventoryItemTypes would be more along the
lines of a unit test.
Unit Tests
Unit tests prove how your database does what it does. If you're using
proper encapsulation there will be certain aspects of a well-designed database that
cannot be tested by a good acceptance test. In addition: If your acceptance
tests target your entire system, then it's going to be really difficult
to cover every aspect of your database design without the use of unit tests.
Unit tests should come and go as requirements change but they should be cohesive
enough that they don't really need to change.
Unit tests should target a group of versions. How you define the grouping
is up to you. A good example would be "This unit test applies to the latest
1.3.x versions of the database." If you define your unit test using that rule,
it will "follow" you for a little while and then, when you move to version 1.4 of
your database, it will "settle in" on the last version before 1.4. Leaving
a unit test intact, and testing an older version of your database gives you
certainty in the history of your database. This is a key element
of growing your database: if you don't know what your database was you
probably don't really know what it is.
Unit tests should prove that you've not only done your job but that you've done
it right. As previously mentioned: certain aspects of your database might
be difficult to test otherwise. For instance, your business services layer
probably already enforces relationships. So, technically, relational integrity
is not required at the database level, right? What if someone, down the line,
decides to write an application that goes directly to the database (which they shouldn't
be doing but... you know). Each part of a system must be whole and correct
in its own right. The server should never trust the client and the database
should never trust the server.
Unit tests give you detailed visibility into the nature of your database and let
you develop certain aspects - such as foreign keys - from a test-driven perspective.
That's great for testing what you have now or what you had in the past, and so are
acceptance tests, but how do we test that our database changes in a controlled, predictable,
and safe way? That's where transition tests come in to play.
Transition Tests
Transition tests are not required for code. The transition usually
goes like this: "uninstall the old, install the new." We can't get away with
that in the database world. When you roll out a change, you are, inherently,
exposing ostensibly valuable data to risk. If something goes wrong, the very
best you can hope for is that a backup will be restored and your sprint is considered
a failure. Transition tests are all about mitigating this risk.
A transition test goes more like: "Assume I have five rows in the
OrderLineItems table with two distinct strings in the ItemType text column. After
the transition, I want to have five OrderLineItems related to two OrderLineItemTypes
rows." They always start by putting the database in one version and populating
it with some data. Then they transition to the very next version
and prove that some change went smoothly.
Transition tests, in conjunction with acceptance and unit tests, complete the database
testing picture. They give you certainty in where you are going
and certainty in how you got where you are. In addition,
they are the kind of test that most often force you to respect production data.
Proper use of all three kinds of tests can give you the same kind of confidence
in your database that you have in your business services layer.
DataConstructor
is designed to facilitate them all.
|