Thursday, April 27, 2006

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.




Visit to Split, Croatia Easter 2006

Photos from our trip to Split for Easter 2006 will be up on Taumuon soon. We got to do quite a lot on this visit - we went to the village where Mare's father grew up, high up in the hills above Split, and took a trip to Mostar and Međugorje in Bosnia and Herzegovina.

The bus to Mostar went down the coast from Split through Omiš (which itself way amazing; there are towering cliffs either side of where the river joins the sea), and we were treated to amazing views of the rising sun lighting up fishing boats in picturesque coves, with crystal clear seas and islands in the background.

Mostar itself was really impressive; Marijana's friend Ivana lives there and gave us a guided tour - the old town is beautiful, and it's quite a weird mixture because of the mix of muslim and non-muslim population. Well, I say mix, but the town is divided between the muslim and catholic population. We got to climb to the top of a mosque's tower, which was vertigo-inducing. The old part of town has markets selling ibriks/cezves/džezve (Turkish coffee pots) and flying carpets (well, that's what they look like). The old town again is an amazing sight, with the bridge (of course) and I hate to use the word picturesque again, but it's too late now.

Also, looking at the photos (when they're up) you might think that there are way too many bombed buildings in Mostar, but there really aren't that many, and there's been lots of redevelopment done, it's just fascinating as the buildings have been left standing as they are, rather than being demolished. It also felt quite morbid to be taking pictures of them all.

Međugorje was a bit of a letdown; admittedly we didn't go into the hills where I'd expect there is a shrine or something, but the town just feels really commercialised.

The other cool thing we did, is to look around the Roman ruins of the town of Solin/Salona, just outside of Split. It was a third of the size of Split is now, almost 2000 years ago. The ampitheature was probably the most impressive thing there.

I also got to hire a mountain bike on the hill Marjan just outside of Split, with Mare's brother Mladen. I wasn't too bad despite having a two year break!

Of course, it was great to catch up with everyone there, and to stuff myself with amazing food and drink again, and to experience UK summer weather in April (can't wait for our next trip!)

This page is powered by Blogger. Isn't yours?