Search This Blog

Sunday 25 April 2010

NUnit - Teamcity - TestCaseSource tip

If you are using features from NUnit 2.5 or above TestCaseSource attribute may sound familiar. This is a very useful feature in NUnit now which helps us reduce the clutter of repetitive code in our test class. I am sure everyone can see how this can be done on the NUnit website as well , but the point of this post is in relation to a problem I ran into when I used the TestCaseSource attribute. To elaborate on this let me use a simple example of a class which calculates the square of an integer.

public class SquareOfNumbers
{
   public static int Square(int x)
{
   return x * x;
}
}

When i first started writing tests i used the TestCaseSource attribute , so my test class looked like the one below

[TestFixture]

public class SquareOfNumberFixture
{
private static readonly Dictionary<int, int> _testData = new Dictionary<int, int>();

public static Dictionary<int, int> TestData
{
   get
   {
       _testData.Add(2, 4);
       _testData.Add(0, 0);
       return _testData;
   }
}

[Test, TestCaseSource("TestData")]
public void Test(KeyValuePair<int, int> keyValuePair)
{
   Assert.That(SquareOfNumbers.Square(keyValuePair.Key ), Is.EqualTo(keyValuePair.Value));
}
}

These tests run successfully, but when run as part of my teamcity build there is no detail about what the tests where as in there was no reference to the name of my test method. I did spend sometime trying to fix this and found a couple of issues.

1. Team city was not using the same nunit version that I was using to write tests, so the plugin had to be updated on team city, this way i made sure the build uses the same nunit framework as my source code does

2. I changed my tests to use TestCaseData class in conjunction with the TestCaseSource attribute, this allowed me to specify a name for each test scenario , so after refactoring the test class , I can now see the test names on the nunit test report generated by Team city, the code for the new test class is shown below

In addition to fixing the problem , TestCaseData class made the test code more readable.

[TestFixture]
public class SquareOfNumberFixture
{
  public static IEnumerable TestData
  {
      get
      {
          TestCaseData squareOfZeroTest = new TestCaseData(0).Returns(0);
          squareOfZeroTest.SetName("SquareOfZeroReturnsZero");
          yield return squareOfZeroTest;

          TestCaseData squareOfIntegerTest = new TestCaseData(2).Returns(4);
          squareOfIntegerTest.SetName("SquareOfTwoReturnsFour");
          yield return squareOfIntegerTest;
      }
  }

  [Test, TestCaseSource("TestData")]
  public int TestSquareOfNumbers(int x)
  {
      return SquareOfNumbers.Square(x);
  }
}

On second thoughts further refactoring the class makes it more readable if i had done the following…

[TestFixture]

public class SquareOfNumberFixtureNew
{
  [TestCase(0, Description = "SquareOfZero", Result = 0)]
  [TestCase(2, Description = "SquareOfTwo", Result = 4)]
  [Test]
  public int TestSquareOfNumbers(int x)
  {
           return SquareOfNumbers.Square(x);
  }
}

1 comment:

Anonymous said...

Or you can just use your own object as input (containing 2 ints) and override the the ToString() method on the object. Not tried in TeamCity yet but I've had similar issues in other tools