Unit testing frameworks not OO / Suggested new NUnit features.

I was looking at the arguments into one assertion per test (not wanting to open a can of worms here), and how the proposed solution shows how testing frameworks lead to non-OO code. In the original blog entry to be found at:

http://www.artima.com/weblogs/viewpost.jsp?thread=35578
http://blog.daveastels.com/articles/category/programming/page/4

Converting the example provided to C#/NUnit (rather than the original Java/JUnit), the test-driven developed Address being tested is:


public class Address
{
 private string addr1;
 private string cityStatePostalCd;
 private string country;

 public Address(string address)
 {
  Parse(address);
 }

 private void Parse(string address)
 {
  string[] tokens = address.Split('$');
  addr1 = tokens[0];
  cityStatePostalCd = tokens[1];
  country = tokens.Length > 2 ? tokens[2] : "";
 }

 public string Addr1
 {
  get { return addr1; }
 }

 public string CityStatePostalCd
 {
  get { return cityStatePostalCd; }
 }

 public string Country
 {
  get { return country; }
 }
}

And the original non-one assertion per test fixture is:


[TestFixture]
public class OriginalFixture
{
 [Test]
 public void TestAddress1()
 {
  Address a = new Address("ADDR1$CITY IL 60563$COUNTRY");
  Assert.AreEqual(a.Addr1, "ADDR1");
  Assert.AreEqual(a.CityStatePostalCd, "CITY IL 60563");
  Assert.AreEqual(a.Country, "COUNTRY");
 }
}

The suggested solution to this is to have new fixtures for each of the assertions:


[TestFixture]
public class Addr1CspTests
{
 private Address anAddress;

 [SetUp]
 public void setUp()
 {
  anAddress = new Address("ADDR1$CITY IL 60563");
 }

 [Test]
 public void TestAddr1()
 {
  Assert.AreEqual("ADDR1", anAddress.Addr1);
 }

 [Test]
 public void TestCsp()
 {
  Assert.AreEqual("CITY IL 60563", anAddress.CityStatePostalCd);
 }
}

[TestFixture]
public class Addr1CspCountryTest
{
 private Address anAddress;

 [SetUp]
 public void setUp()
 {
  anAddress = new Address("ADDR1$CITY IL 60563$COUNTRY");
 }

 [Test]
 public void TestAddr1()
 {
  Assert.AreEqual("ADDR1", anAddress.Addr1);
 }

 [Test]
 public void TestCsp()
 {
  Assert.AreEqual("CITY IL 60563", anAddress.CityStatePostalCd);
 }

 [TestAttribute]
 public void TestCountry()
 {
  Assert.AreEqual("COUNTRY", anAddress.Country);
 }
}

The author suggest as the Addr and Csp tests are similar in both cases, then they could be refactored into a base class.

However, this got me thinking about the test code I’ve written/worked with – it’s usually quite script like and not very OO, and this example shows why – even though it leads to the definition of more classes, it’s not really OO – each of the classes corresponds with an instance that is instantiated by the test framework. And each of the instances doesn’t differ in their operation, but only in the data used in the construction and the test methods.

It would be tidier if we could instantiate these test fixtures with the data required, and allow NUnit to add them to the list of fixtures and maintain them. I think MBUnit lets you somehow define what data the tests will use via XML, which may do the trick.

So what I’m thinking is is that -somewhere-, I’m not sure where would be best, there would be a method decorated with a TestFixtureProviderAttribute, and this would return a set of TestFixtures when called – so no longer would the TestFixtures only be created using their default constructors.

In our case, the above example would be replaced by something like (excuse any syntax errors):


[TestFixture]
public class AddrTest
{
 private Address anAddress;

 public AddrTest(string address, string expectedAddr1, string expectedCsp, string expectedCounty)
 {
  ...// assign to members
 }

 [SetUp]
 public void setUp()
 {
  anAddress = new Address(address);
 }

 [Test]
 public void TestAddr1()
 {
  Assert.AreEqual(expectedAddress, anAddress.Addr1);
 
}
 [Test]
 public void TestCsp()
 {
  Assert.AreEqual(expectedCsp,anAddress.CityStatePostalCd);
 }

 [TestAttribute]
 public void TestCountry()
 {
  Assert.AreEqual(expectedCountry,anAddress.Country);
 }
}

And this would be created by:


 [TestFixtureProvider]
 ArrayList GetTestFixtures()
 {
  ArrayList fixtures = new ArrayList();
  fixtures.Add(new AddrTest("ADDR1$CITY IL 60563", "ADDR1",
    "CITY IL 60563", "");
  fixtures.Add(new AddrTest("ADDR1$CITY IL 60563$COUNTRY", "ADDR1",
    "CITY IL 60563", "COUNTRY");
  return fixtures;
 }

This would be more OO – the data could be easily populated from an XML file, or created manually, and more importantly, to add a test case wouldn’t require creating a whole new Class definition. This would stop the profileration of classes problem caused by test fixtures leading to non-OO code. If anyone’s reading this, and cares, I can try modifying the NUnit sources to investigate further.