Friday, February 24, 2006

Unit testing and the database

I have been playing with unit testing for over a year now and really see the value in it. Most of the time you build your tests along with (or before in the case of test driven development) your code. But I have even found it valuable to write tests for code I have already written in the past if I am going to have to make a change. I usually only write a test for the method I am about to change, make my change and then see if my test still passes.

One of the things that I hate is how many articles recommend to mock the database so that no database calls are made. The reason for this is that database interaction is a performance killer. Second of all it loads the database with tons of repeated test data (unless you perform a cleanup in your test case that is).

I feel that it is invaluable to have your tests hit the database. I have had cases where all of the business rules pass but when it hits the database an exception is thrown. If I had mocked the database I would not know about this. Granted it is slow to do it this way but I would rather be slow than buggy.

As far as loading the database with repeated test data there are a couple of ways around it. The first is to have every test clean its footprint up which is a big pain. The second way is to enlist the test in a distributed transaction, running the test, and then rolling back the transaction. The way I do this is have a base class that all of my tests inherit from and then have the enlist / rollback in the setup and teardown methods respectively. Here is my standard TestBase class

1 Imports NUnit.Framework

2 Imports System.EnterpriseServices


4 _

5 Public Class TestBase


7 _

8 Public Sub Setup()

9 ' Enter a new transaction without inheriting from ServicedComponent

10 Console.WriteLine("Attempting to enter a transactional context...")

11 Dim config As New ServiceConfig

12 config.Transaction = TransactionOption.RequiresNew

13 ServiceDomain.Enter(config)

14 Console.WriteLine("Attempt suceeded!")

15 End Sub


17 _

18 Public Sub Teardown()

19 Console.WriteLine("Attempting to Leave transactional context...")

20 If (ContextUtil.IsInTransaction) Then


22 ContextUtil.SetAbort()

23 End If

24 ServiceDomain.Leave()

25 Console.WriteLine("Left context!")

26 End Sub

27 End Class


The ServiceDomain class is something most people don't know about and is quite handy to ccreate a localized transaction. You can see that it creates a config that requires a new transaction and then calls ServiceDomain.Enter(config). This changes the context of the application to change and participate in a transaction.

In our tear down we check if we are in a transaction with ContextUtil and then Abort the transaction if we are in it. This will cause all of our interactions with anything that can use DTC to rollback.

So now you can test your database and not worry about garbage data all over your system. I like this as when I put an applicaiton into production I can run my tests against it and double check that no enviromental errors occured without introducing garbage data.


Post a Comment

<< Home