Products

Solutions

Resources

Partners

Community

About

New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.


Adventures in Testing – 7 – Testing Interfaces with MbUnit using the Factory Attribute

I haven’t posted for a while in my irregular series on testing and over the weekend while I was struggling with creating some tests in a personal programming project I discovered an awesome feature of MbUnit.

Early in 2010, we (DotNetNuke Corporation) decided to standardize on MbUnit as our testing framework.  Many of my own projects had been written using MSTest (the framework that ships as part of Visual Studio Team System) and this weekend I decided to port one of my older projects to use this testing framework, as I am planning to do some more work on it. 

In this project I had an abstract base class with two different implementations – something quite common in DotNetNuke (DNN) with our extensive use of the Provider Pattern.  I had written quite a few tests in MSTest and I was frustrated that in order to write tests for both concrete implementations I had to do a lot of Copy/Paste inheritance. 

In the code was a comment – “TODO: I don’t like duplicating this code – there must be a better way”.  I tried using a common Test base class for each of the implementations but then all my Test logic was in a different class from the Test Fixture – which made it harder to determine what the test was actually doing.

Enter MbUnit’s ability to test Interfaces - which essentially allows you to write tests against a base type (either an interface or an abstract base class) and re-run the same tests for every implementation.

Let’s look at how this works.  Rather than create an artificial example, lets look at some core .NET collection classes.  The ArrayList and Hashtable classes both implement the IEnumerable interface. The IEnumerable interface has one method GetEnumerator, and we basically only need to test that this method does not return null (Listing 1)

Listing 1 – Testing ArrayList’s GetEnumerator method

   1:  [TestFixture]
   2:  public class ArrayListTests
   3:  {
   4:      [Test]
   5:      public void GetEnumerator_Does_Not_Return_Null()
   6:      {
   7:          //Arrange
   8:          var collection = new ArrayList();
   9:   
  10:          //Act and Assert
  11:          Assert.IsNotNull(collection.GetEnumerator());
  12:      }
  13:  }

In order to write the same test for a Hashtable we would need to write a completely different test that instantiates a Hashtable instead of an ArrayList (Listing 2).

Listing 2 – Testing Hashtable’s GetEnumerator method

   1:  [TestFixture]
   2:  public class HashtableTests
   3:  {
   4:      [Test]
   5:      public void GetEnumerator_Does_Not_Return_Null()
   6:      {
   7:          //Arrange
   8:          var collection = new Hashtable();
   9:   
  10:          //Act and Assert
  11:          Assert.IsNotNull(collection.GetEnumerator());
  12:      }
  13:  }

And this is what triggered my original code comment.  This is not very elegant as I have just copy/pasted a whole bunch of code and it is not trivial to refactor.

Also, what if I wanted to test that the method did not return null regardless of whether the collection had any members.  In MSTest I would have to create a whole new method to test this other scenario – another horrible bit of copy/paste inheritance.

We have already seen in this series that MbUnit has the ability to run multiple tests with different input parameters by using the Row attribute.  MbUnit also has the ability to use a Factory attribute to generate multiple instances of objects that a suite of tests can be run against.  This is how we can handle multiple concrete implementations of interfaces and abstract classes (Listing 3).

Listing 3 – Using MbUnit’s Factory attribute

   1:  public class IEnumerableTests
   2:  {
   3:      public static IEnumerable GetInstances()
   4:      {
   5:          yield return new ArrayList();
   6:          yield return new ArrayList { 0, 1 };
   7:          yield return new Hashtable();
   8:          yield return new Hashtable { { "a", 0 }, { "b", 1 } };
   9:      }
  10:   
  11:   
  12:      [Factory("GetInstances")]
  13:      public IEnumerable Instance;
  14:   
  15:      [Test]
  16:      public void GetEnumerator_Does_Not_Return_Null()
  17:      {
  18:          Assert.IsNotNull(Instance.GetEnumerator());
  19:      }
  20:  }

This is much better.  We still have a simple, understandable Test method “Get_Enumerator_Does_Not_Return_Null”.  The Instance variable is decorated with a Factory attribute, which specifies the “GetInstances” method as the Factory Provider.  The end result is that the test is run four times – one for each IEnumerable instance created by the “GetInstances” method.  If we decide that we need to test another implementation then we just need to add a couple of statements to the “GetInstances” method.

This example was trivial – there was only one test – but what if you have a complex interface or abstract base class with a large number of tests.  In this situation this Factory attribute reduces the amount of work dramatically.  If there are any implementation specific cases that need to be tested they could still be done the traditional way, but this approach allows us to handle most (if not all) of the shared test cases.

This article is cross-posted from my personal blog

Comments

There are currently no comments, be the first to post one.

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out