How to extend NUnit to support transaction rollback?



By ganton ~ October 30th, 2008. Filed under: Unit testing.

The code for this article you can download here.

I create some unit tests which should test functionality connected with databases. I know that there are a lot of posts about not to test against database but from my experience I think it is better than using Mocks instead of. What I want is to have an option to say that some of the test methods should rollback what they’ve change in DB. After some googling I found that NUnit can be extended. Unfortunately, information connected with this topic on NUnit web site is not enough but I found a nice article by Ben Hall.

I decide to implement an add-in that will support Rollback attribute and will execute the test under TransactionScope. For this purpose I created a new Dll which contains 3 classes. A RollbackAttributre class which doesn’t allow multiple and can be applied only on methods.


[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RollbackAttribute : Attribute
{

}

Further I created ExecuteTestTransactionScopeMethod whinc inherites from NUnitTestMethod class and overrides one of its Run methods. There I run the test into a TransactionScope.


public class ExecuteTestTransactionScopeMethod : NUnitTestMethod
{
public ExecuteTestTransactionScopeMethod(MethodInfo method) : base(method) { }

public override void Run(TestCaseResult testResult)
{
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
base.Run(testResult);
}
}
catch (TransactionException exception)
{
testResult.Failure(exception.Message, exception.StackTrace);
}
}
}

Then I created a TransactionScopeAddin class which implements IAddin and ITestDecorator interfaces. Both interfaces belongs to NUnit.Core.Extensibility namespace. The first one is needed in order to note NUnit that this is an add-in and also I need it in order to be able to install my add-in. It is done implementing IAddin.Install method. As you can found in posts that I mentioned above NUnit can be extended in four different ways using Event listeners, Suite builders, Test case builders and Test decorators. I’ve decided to use a test decorator NUnit’s extension and that is why my class implements ITestDecorator. In Decorate method I try to find Rollback attribute if such exists I return a new instance of ExecuteTestTransactionScopeMethod class.


[NUnitAddin(Description = "Transaction Scope Addin", Type = ExtensionType.Core)]
public class TransactionScopeAddin : IAddin, ITestDecorator
{
#region IAddin Members

public bool Install(IExtensionHost host)
{
IExtensionPoint decorators = host.GetExtensionPoint("TestDecorators");
if (decorators == null)
{
return false;
}
decorators.Install(this);
return true;
}

#endregion

#region ITestDecorator Members

public NUnit.Core.Test Decorate(NUnit.Core.Test test, System.Reflection.MemberInfo member)
{
NUnitTestMethod nunitTestMethod = test as NUnitTestMethod;
if (nunitTestMethod != null)
{
Attribute ignore = Reflect.GetAttribute(member, "Ignore", false);
if (ignore != null)
{
return test;
}
object[] attributes = member.GetCustomAttributes(typeof(RollbackAttribute), false);
foreach (var attribute in attributes)
{
RollbackAttribute ra = attribute as RollbackAttribute;
if (ra != null)
{
return new ExecuteTestTransactionScopeMethod(nunitTestMethod.Method);
}
}
}
return test;
}

#endregion
}

I had some problems with testing this add-in because new version no always were taken into account from NUnit. It is just my feeling I’m not sure that the real problem was with NUnit. But according their specification I just should place my dlls into NUnit bin\addin directory.

After all was OK I received some exceptions about allowing network MSDTC access. After some investigation over the problem I found how to allow such access to DTC. Using Component Services console one can change DTC configuration. In order to allow network DTC access one should go to Component Services/Computers/My Computer context menu Properties.

Thin under MSDTC tab there is a button named Security Configuration. Go there and you will be able to configure network DTC access.

Below you can see how I configured my MSDTC in order to allow network access.

After some time I was using this add-in and Rollback attribute I can say that for some reason TransactionScope works unpredictable and sometimes some of the tests crashed with a transaction error. I’m not sure that it is directly connected with System.Transaction classes because I googled information that shows that such problems can be connected with NHibernate when we use TransactionScope with it. I’m about to do the same tests with MS Entity Framework and I will see if the same errors will occur with it.

But for our project where we use NHibernate I’ve decided to not use current add-in (for now) and I created two classes which allows us to test against database and keep it in an initial state. About this solution you can read in my next post.

2 Responses to How to extend NUnit to support transaction rollback?

  1. Execute unit tests against DB and keep it unchanged. » Anton Gochev’s Weblog

    [...] my old post I explained how you can create a NUnit add-in for reverting changes in database made by unit tests. [...]

  2. Anton Gochev’s Weblog » Blog Archive » Execute unit tests against DB and keep it unchanged using AOP

    [...] How to extend NUnit to support transaction rollback? [...]

Leave a Reply