Showing posts with label NUnit. Show all posts
Showing posts with label NUnit. Show all posts

Friday, 16 April 2010

NUnit Part 3: Exception Handling

This is going to be very short (at least compared to the last couple of posts.) So far we have seen how to write a some basic tests using assertions and assign set up/clean up methods. But what if we want to check that a call throws an exception? We could wrap our test in a try-catch-finally block. We would have to assert:

1. That the expected exception was thrown (test in the catch block), and
2. That an exception was thrown at all (test in the finally block)

That's an awful lot of extra code to write. Tests are meant to be small. Fortunately we have a way round this - we just add the attribute [ExpectedException()].

A perfect example would be checking that calling the divide method of our calculator with a zero argument throws a DivideByZeroException. As with our earlier tests, this is really easy:



using System;
using NUnit.Framework;

namespace WindowsGame1
{
[TestFixture]
public class Calculator_Tests
{

// Omitted earlier code...

[Test]
[ExpectedException(typeof(DivideByZeroException))]
public void Divide_By_Zero_Should_Throw_Exception()
{
Calculator.DivideBy( 0 );
}
}
}



No assertions. No try-catch-finally blocks. Just one line of code (well, two if you count the attribute). Note we added the System namespace to our test class, to simplify the use of exceptions. As long as the expected exception is thrown, the test will pass. If a different one is thrown, it will fail. Likewise, if no exception is thrown it will fail.

We now have the basics in place to test boundary conditions as well. But our tests are still very basic. In order to really get to grips with testing, we would like to examine what a method is actually doing internally (both to its arguments and its own state objects). We solve this with stubs, fakes and mocks. But before we can get to that, we'll need to introduce two important coding practices:

1. Design to an interface.
2. Dependency Injection

Stay tuned!

NUnit Part 2: Setup Methods

In my previous post I gave a very brief introduction to using NUnit. Unfortunately, our current approach requires us to write quite a lot of repetitive setup code at the start of each tests. In our examples this is limited to creating our calculator and setting its initial value. But when we introduce Dependency Injection in a couple of posts time, we'll see that this setup code can become quite involved.

Fortunately, NUnit provides us with four very useful attributes we can apply to methods to simplify this process:

1. [TestFixtureSetUp]
2. [TestFixtureTearDown]
3. [SetUp]
4. [TearDown]

The first two are [TestFixtureSetUp] and [TestFixtureTearDown]. We attach these to methods that we want to be called once - one at the start of all the tests, the other after all the tests. This way we can create our persistent objects (for instance, the calculator) and also safely delete them (for instance, if they have some code that should be called before they go out of scope).

The second two should be attached to methods that are called before and after each test. this is where we can create/destroy temporary objects and reset the state of permanent ones.

Time to put this into practice. We're going to extend our calculator class, so that it can also subtract and multiply (we'll get to division in the next post). Rather than walk you through essentially a repetition of the last post, here's the code for the calculator:



namespace WindowsGame1
{
public class Calculator
{
public Calculator()
{
this.CurrentValue = 0;
}

public void Add( int aNumber )
{
this.CurrentValue += aNumber;
}
public void Subtract( int aNumber )
{
this.CurrentValue -= aNumber;
}
public void Multiply( int aNumber )
{
this.CurrentValue *= aNumber;
}

public int CurrentValue
{
get;
set;
}
}
}



and the tests:



using NUnit.Framework;

namespace WindowsGame1
{
[TestFixture]
public class Calculator_Tests
{
[Test]
public void Should_Add_Integers()
{
Calculator calculator = new Calculator();
calculator.CurrentValue = 1;

calculator.Add( 2 );
Assert.AreEqual( 3 , calculator.CurrentValue );

calculator.Add( 3 );
Assert.AreEqual( 6 , calculator.CurrentValue );

calculator.Add( -4 );
Assert.AreEqual( 2 , calculator.CurrentValue );
}

[Test]
public void Should_Subtract_Integers()
{
Calculator calculator = new Calculator();
calculator.CurrentValue = 1;

calculator.Subtract( 2 );
Assert.AreEqual( -1 , calculator.CurrentValue );

calculator.Subtract( 3 );
Assert.AreEqual( -4 , calculator.CurrentValue );

calculator.Subtract( -4 );
Assert.AreEqual( 0 , calculator.CurrentValue );
}

[Test]
public void Should_Multiply_Integers()
{
Calculator calculator = new Calculator();
calculator.CurrentValue = 1;

calculator.Multiply( 2 );
Assert.AreEqual( 2 , calculator.CurrentValue );

calculator.Multiply( 3 );
Assert.AreEqual( 6 , calculator.CurrentValue );

calculator.Multiply( -4 );
Assert.AreEqual( -24 , calculator.CurrentValue );
}
}
}



We can simplify our tests (slightly) by extracting the setup code:



using NUnit.Framework;

namespace WindowsGame1
{
[TestFixture]
public class Calculator_Tests
{
[TestFixtureSetUp]
public void TestFixtureSetup()
{
Calculator = new Calculator();
}

[SetUp]
public void PreTest()
{
Calculator.CurrentValue = 1;
}

[Test]
public void Should_Add_Integers()
{
Calculator.Add( 2 );
Assert.AreEqual( 3 , Calculator.CurrentValue );

Calculator.Add( 3 );
Assert.AreEqual( 6 , Calculator.CurrentValue );

Calculator.Add( -4 );
Assert.AreEqual( 2 , Calculator.CurrentValue );
}

[Test]
public void Should_Subtract_Integers()
{
Calculator.Subtract( 2 );
Assert.AreEqual( -1 , Calculator.CurrentValue );

Calculator.Subtract( 3 );
Assert.AreEqual( -4 , Calculator.CurrentValue );

Calculator.Subtract( -4 );
Assert.AreEqual( 0 , Calculator.CurrentValue );
}

[Test]
public void Should_Multiply_Integers()
{
Calculator.Multiply( 2 );
Assert.AreEqual( 2 , Calculator.CurrentValue );

Calculator.Multiply( 3 );
Assert.AreEqual( 6 , Calculator.CurrentValue );

Calculator.Multiply( -4 );
Assert.AreEqual( -24 , Calculator.CurrentValue );
}

private Calculator Calculator
{
get;
set;
}
}
}



Granted, our example doesn't really call for the use of setup methods. But future examples will. In particular, the first chapter on XNGen's creation - the input system - which we will start in 5 posts time. In the meantime, remember that it's not just our product code that we can refactor, but our tests too.

Thursday, 15 April 2010

NUnit Part 1: A Testing Framework For C#

So if you've read my last post (or if you've come across testing before) you'll hopefully be sold on TDD and Unit Testing. But how do we actually go about testing code in C#?

Most coders use a version of the XUnit framework to test their code. For C# this means NUnit. Simply download the installer, run it and add a reference to it in your project. In Visual C# Express, simply right click on your project, add a reference and browse to the NUnit\bin\net-2.0\framework folder. Add the nunit.framework dll and we're done (you wont need the other dlls until later). That was easy!

Now that our project knows where to find NUnit, we can write our first tests. For this example, we're going to start making a simple calculator in C#. Add a new class to your project called "Calculator_Tests". When I test a class called "X" I like to call the testing class "X_Tests". Normally I try to avoid using underscores in names, but I've grudgingly accepted the wisdom of this approach (we'll see why when we run our tests). Add the following to the class:



You'll need to replace WindowsGame1 with your projects name. The using statement lets the class know about NUnit. The [TestFixture] attribute tells NUnit that the class contains tests. Similarly, the [Test] attribute tells NUnit that it should run the following method. The Assert class is part of NUnit and is how we test results. It has many methods (AreSame checks if two references are actually to the same object, AreEquals checks their equality, IsTrue expects true statements etc...) The test passes if all its asserts pass. On the other hand, if even one assert fails so does the test.

Note the naming convention for the test. Once again we are using underscores. This is simply to make the results of our tests easier to read and is by no means required.

Now try to compile the project. Obviously we get some errors as we haven't written the Calculator class yet. But compile time errors are a Good Thing&trade. This way, we don't accidentally ship something that's broken. So lets add the minimum amount of code required to compile:



Again, you'll need to rename WindowsGame1 to your project name. Hit compile and we're done. Right? No? But according to the compiler our code is fine? Obviously our add method is wrong. To proove it lets run our test. Open up the NUnit gui and load our project (it should be in our projects bin folder - probably bin\x86\Debug\). Hit run and you'll see a big red cross next to our test name (see why the odd naming convention was recommended?) Hmm... lets fix that! Change our calculator class to:



Here we've demonstrated a key point about TDD - always do the simplest thing required to pass a test. Sure we'll miss some important boundary testing (null values etc...) but we can cover all that in our unit tests. For now, lets just get the code working!
Compile and run our test. Green light! So we're finished right? Well... not quite. We've still not done our refactoring yet. The problem is the unnamed constant (3). Unnamed constants are a Bad Thing&trade. Coders will often talk about bad smells - that is code that appears to work, but doesn't feel right - and an unnamed constant really stinks. But at least we have a fall back solution that we know makes our tests works. Lets try the (hopefully obvious) solution:



Compile, test and green light. OK, so we've not checked our boundry cases (for instance calculator.Add( int.MaxValue )) but for now we're good. We can improove our confidence in our method by adding to our smoke test:



Now we can delete the commented out line from the Add method. And we're finally done. Hopefully you've seen that tests are easy to implement (if a little pointless in this case) and that TDD can be quite painless. In the next example I'll show you how to reduce the amount of test code you have to write using the setup and teardown methods. Stay tuned.