Friday 16 April 2010

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.

No comments:

Post a Comment