This is something that has made my integration tests better. You define a test method to run in transaction and rollback at the end of each test. Doesn’t matter does the test pass or fail, it will rollback the transaction after it has run. This makes it possible to write integration tests that write to the database. And tests won’t leave any “garbage” there because rollback at the end of the test undoes all database updates (and inserts and deletes). If this is possible in your language and testing framework I strongly recommend it. In short, it is like this:
MyTestMethodWithAutoRollback() { // 1) Begins transcation automatically before the test // 2) Run your integration test normally } // 3) Transaction is automatically rollbacked after the test
You don’t have to put any `finally` block to clean updates that are made to the database. I will tell how this is possible with NUnit.
NUnit and Rollback Attribute
With NUnit, you can define a Rollback attribute. Trailmax Tech blog explains how it is implemented:
public class RollbackAttribute : Attribute, ITestAction // (1), (2) { private TransactionScope transaction; public void BeforeTest(TestDetails testDetails) { transaction = new TransactionScope(); // (3) } public void AfterTest(TestDetails testDetails) { transaction.Dispose(); // (4) } public ActionTargets Targets { get { return ActionTargets.Test; } // (5) } }
I will explain this class a bit:
- Derive System.Attribute to make this class an attribute (1)
- Implement NUnit’s ITestAction interface to get access to NUnit’s process (2)
- BeforeTest and AfterTest methods and Targets property
- Begin new transaction before the test (3)
- Rollback (dispose) transaction after the test (4)
- ActionTargets.Test tells that these BeforeTest and AfterTest methods will be run for each test (5).
Here is an example of using Rollback attribute with integration test with Petapoco:
[Test, Rollback] public void InsertTest() { var testPoco = new TestPoco(name: "MyPoco"); var db = new Petapoco.Database("ConnectionStringName"); db.Insert(testPoco); Assert.IsTrue(testPoco.Id != 0, "Didn't save TestPoco because Id isn't setted"); } // Rollback attribute makes rollback (dispose) called after test automatically. // At this point saved TestPoco is rollbacked and it is cleared from the database.
As you can see it is really easy to implement integration tests with Rollback attribute (line 1). With it, you don’t have to care about writing to the database because all is rollbacked after the test.
MSTest and Rollback Class
With MSTest it is little different compared to NUnit. First, implement a Rollback class that makes rollback after each test method. Then derive that Rollback class in your test class. For details see this Stack Overflow answer.
Conclusion
This technique has made my integration tests much better. For example, when you have legacy code, you can’t always start by implementing unit tests. Practically you have to begin with integration tests before you can refactor the code to more unit test friendly.
In my opinion, it is good to have both unit and integration tests, but more unit tests. Integration tests will complete unit tests. I want to be sure that reading and writing to the database also works after changes and there you will need integration tests.
You can also write your own “clean up” code that will delete inserted rows etc. from the database at the end of the test. But you have to maintain it if you change your code. And you can’t be sure if some method has written to the database that you don’t know. With automatic rollback, you don’t have to maintain any “clean up” codes.
Automatic rollback makes it a lot easier to write integration tests that write to the database.
Sources
- Trailmax Tech blog by Max Vasilyev. “Rollback attribute for NUnit and Entity framework”
- overtest.me blog. “Creating custom attributes with internal NUnit features”
- NUnit documentation. “Implementing an Action Attribute”
One thought on “Automatic Rollback to Integration Tests in C#”