I have found three principles that really improve the stability and maintainability of our integration tests that hit the database. These principles are intended for automated tests running through some unit testing framework that access the database in any way.
Data Self-Sufficiency
A test should insert any data it needs into the database. The converse is true: A test should not depend upon pre-existing data in the database.
A common problem with test frameworks that depend upon data in the database is that they are fragile. Any change to the underlying data and the test fails. What makes it even more of a problem is that the failure is a “false-negativeâ€. There is nothing wrong with the code, but it fails. A test that inserts its own data will not suffer from this fragility because the data it depends on cannot be changed without modifying the test itself.
Intrinsic Data Assurance
For data that is intrinsic to the system (required for operation), tests should be written to assert its existence.
Some data simply MUST be in the database for the application to function. This data is “intrinsic†to the operation the application. Because the application will fail if this data does not exists, we should write tests asserting its existence. If this required data ever changes or disappears, our tests will immediately notify us and we can correct the issues.
Side-Effect Free
A test should rollback any changes it makes to the database regardless of whether the test passes or fails.
This principle really just tells you what your mom always said, “clean up after yourselfâ€. Don’t leave garbage behind. Your database should not be littered with test data from previous test runs. The easiest way to achieve this is to run the test in a transaction that is rolled back upon completion. I do this using a base class for all database tests called AutoRollbackDatabaseTest. The SetUp() method starts the transaction and the TearDown() rolls it back.
An additional advantage to following this principle is that you are able to run your tests in any environment without the fear of introducing dirty data into that environment. For instance, the tests can be run against DEV, TEST, or PRODUCTION.
In conclusion, the goal behind these principles “The Autonomous Testâ€: that each test is self-contained and independent of external constraints. An Autonomous Test is resilient to changes in the system around it, resulting in lower maintenance effort and higher reliability. Following these principles is one way to help drive toward that goal.
-=CE=-