Thursday, May 08, 2008

NUnit Best Practices

Programming is not just an art, not just a science, but a discipline. Part of what we all know we should be doing includes solid unit testing. Below is my list of best practices, feel free to comment and add others I enjoy feedback.

Create a separate assembly for your test fixtures

i.e. don't be lazy and put your test fixtures in the same assembly as the application code)

Create a bin folder per solution and place nunit.framework.dll in there

Each project should copy their binaries to the solution bin folder. Your Post-Build event should look like this: copy $(TargetFileName) "../../../bin/"

Categorize your test fixtures such as DAO, ETL and BusinessRules. This will let you test pieces of your application more easily: [Category("ETL")]

Once you have the basic interface of a class written, it is time to write your unit test for it. Even though it will fail it will at least serve as a place holder to go back to as well as serving as a test for when things are working

Make sure each test is atomic. Therefore if you are inserting data into a table and you know say CustomerName must be unique, then the second time you run your test it will fail because of uniqueness. For this reason have your persistence tests be all inclusive, include an insert, update and delete. Put your delete code into a finally so that it is always executed regardless of if the other tests succeed or not

If you notice all of your test fixtures perform certain setup every time, create a base class for this, let's say each one of your DAO classes will be instantiating a connection and opening it before starting and this connection needs to be closed at the end of testing whether it fails or succeeds

Obviously we would never want to handle connection objects in our Unit Tests, this would be bad design, in the real world it may be a stream or nhibernate session that you are configuring.

[TestFixture]public class DAOBaseFixture{SqlConnection cn;

[SetUp]public void Init(){ cn = new SqlConnection(); cn.Open("...");}

[TearDown]public Cleanup(){ cn.Close();}

}

Now derivatives of this class won't even have to implement the [TestFixture] attribute nor take care of their setup. They will be free to just take care of their own tests.

public class CustomerDAOTests : DAOBaseFixture

{

[Test]

[Category("DAO")]

public void FindAll()

{...}

}

I still recommend to people writing .Net code to use NUnit. First it's free and you don't have to own any version of Visual Studio to support it. Therefore if you have programmers or testers offshore who only have Visual Studio Pro they can still run it. You don't even need a Visual Studio license to run it. Finally it's a very mature product and it's semantics are well accepted and somewhat portable from language to language.

9 comments:

I slightly disagree with your point #7: I think it's a good thing to let my test classes expose everything it does. I consider base setups and teardowns a code smell.

In other words, when it comes to test classes, it's ok to inherit common implementation, but not common usage. Whenever my test class opens a database connection, I want to see it in the test - not in the base.

For step 2, it's nice to have a global library directory in source control with the NUnit binaries in there (Pragmatic Unit Testing recommends something similar).

Step 3 I don't quite get; we tend to create a separate project in the same solution for the unit test project. It references the main project using a project reference. There's no need for a post-build event.

on #7I think your approach is valid. However I would argue that if you setup you connection in your setup & teardown that it is part of you test since these are executed if you just run one test. Also there's no reason you couldn't expose the setup/teardown methods in your base class

on #3By having all your bins necessary in a central directory someone can run all your tests whether they are using just one project or even if they are not a programmer at all. You could also have your build server check in the latest build to this location into your source code control as well.

I'm not quite sure whether I understand #5. Generally I would say that test-driven developement is about driving the design of the code. This includes the interfaces, and maybe in particular the interfaces. In other words I would start with the tests even for driving out the interface of a class. - Maybe I'm overlooking something. What is the reason that you create the "basic interface" first? What do you mean with "basic interface"?

"Create a separate assembly for your test fixtures"Then I can only test public methods from my test code. Yikes. One of the problems with Microsoft's nUnit-wanna-be implementation is this very fact. And for that very reason our company sticks with nUnit. Making something public should take great consideration. Making it public so it can be tested....ouch!

In all honesty, I really don't like the word "Best Practices" even though I used it, ;-). In retrospect it's not the correct title, 'NUnit recipes' would have been more accurate. Thanks for the feedback