Click here to Skip to main content
15,860,844 members
Articles / Programming Languages / C#

Composite Unit Testing with MbUnit

Rate me:
Please Sign up or sign in to vote.
4.69/5 (15 votes)
16 May 20044 min read 96K   109   38   15
A new way of building test fixtures by taking advantage of interface compositions
In this article, you will learn a new technique called Composite Unit Testing to design and implement unit tests. You will see the advantages of this approach, followed by an illustration of this technique on ArrayList and Hashtable.

Introduction

This article presents a new way of creating unit tests. Rather than creating a fixture for each class, we split the testing effort by class functionality. Taking advantage of interface composition, we use split the unit tests for each interface and we feed those fixtures using factories. This is why I call this technique: Composite Unit Testing.

There are several advantages of using this approach:

  1. Expressing requirements: Interfaces are a natural place for expressing requirements, which later on translate into unit tests.
  2. Test reusability: Once you have designed a test suite for an interface, you can apply it to any class that implements this interface.
  3. Test Driven Development: This approach fits nicely and naturally into the TDD paradigm since execution path is interface -> interface fixture -> implementation(s).
  4. Separation of tests and tested instance generation: The code that generates the tested instances is located in factories that can be reused for each fixtures.

In the rest of the article, I will illustrate this technique on ArrayList and Hashtable.

Test Case

Let us illustrate the process with two classes of the System.Collections namespace: ArrayList and Hashtable.

The two classes belong to different families of containers: ArrayList is a sequential container, while Hashtable is an associative container. However, as the interface diagram below shows, they share a lot of functionalities (enumeration, cloneable, serialization, etc.). These functionalities are usually represented by interface composition: ICloneable, ISerializable, etc. The interface defines functionalities and requirements on those functionalities.

ArrayList and Hashtable

If you take the usual unit testing methodology, you will need to write two (huge) fixture to test the two classes. Since they share functionality, you will end up duplicating testing code, maintenance problem will increase, etc.

Composite unit testing provides a flexible solution to those problems. In the following, I will illustrate how it is implemented in MbUnit.

Composite Unit Testing Methodology

As mentioned in the introduction, composite unit testing fits naturally in the TDD idea. The principal steps of the process are

  1. create the interface and express requirements,
  2. create the interface fixture and translate the requirements into unit tests,
  3. implement the interface,
  4. create a class factory that provides instances of the interface,
  5. link fixture to factories and run...

Step 1: Create the Interface

This is where you define the functionalities and the requirements. If the documentation is clear enough, it should translate naturally into unit tests. (In this example, the job is already done).

Step 2: Create the Interface Fixture (for IEnumerable)

MbUnit defines a new custom attribute TypeFixture that is used to create fixture for types (classes, structs or interface). TypeFixture constructor take the type that is tested as argument. Let us start with the fixture of IEnumerable:

EnumerableTest

C#
using System;
using System.Collections;
using MbUnit.Core.Framework;
using MbUnit.Framework;

[TypeFixture(typeof(IEnumerable))]
public class EnumerableTest
{}

The test case in EnumerableTest will receive an instance of the tested type (IEnumerable here) as argument. Therefore, the correct signature of those methods is as follows:

C#
[TypeFixture(typeof(IEnumerable))]
public class EnumerableTest
{
    [Test]
    public void EmptyTest(IEnumerable en)
    {...}
}

The argument is the only difference with the "classic" unit test. You can use test decorators like ExpectedException, Ignore, etc. as usual. IEnumerable defines one method, GetEnumerator. The only requirement is that the IEnumerator instance is not a null reference:

C#
[TypeFixture(typeof(IEnumerable))]
public class EnumerableTest
{
    [Test]
    public void GetEnumeratorNotNull(IEnumerable en)
    {
        Assert.IsNotNull(en.GetEnumerator());
    }
}

That's pretty short but there is nothing else to test. If you want to test the enumeration, you need to write another fixture for IEnumerator. By defining fixtures for each interface, you quickly increase the coverage of the tested code.

Composition of tests

Step 4: Create the Factories

(We have skipped step 3, the implementation step.)

A factory is simply a class that defines public properties or method (with no arguments) that return an object to be tested. You can use factories to provide different flavor of the same class: an empty ArrayList, randomly filled, ordered filled, etc. For example, a possible factory for ArrayList is:

C#
public class ArrayListFactory
{
    public ArrayList Empty
    {
         get
         {
             return new ArrayList();
         }
    } 
    public ArrayList RandomFilled()
    {
         ArrayList list = new ArrayList();
         Random rnd = ...;
         for(int i=0;i<15;++i)        
             list.Add(rnd.Next());
         return list;
    } 
}

Note that a factory does not need any particular attributes. Similarly, we can define HashtableFactory.

Step 5: Linking the Fixtures to the Factories

Linking the factories to the fixtures is simply done by using another custom attribute: ProviderFactory.

C#
[TypeFixture(typeof(IEnumerable))]
[ProviderFactory(typeof(ArrayListFactory),typeof(IEnumerable))]
[ProviderFactory(typeof(HashtableFactory),typeof(IEnumerable))]
public class EnumerableTest
{...}

ProviderFactory takes the type of the factory, and the tested type as argument. The framework will take care of exploring by reflection the factories, select the suitable properties and feed the fixtures with created test instances.

Factories

Step 6: Running the Tests

The full source of the example is available in the demo project. You need to create a new C# assembly project and add the reference to MbUnit.Core.dll and MbUnit.Framework.dll, which you can download from the MbUnit web site: http://mbunit.tigris.org.%20/.

Launch MbUnit.GUI and load the assembly (right click -> Assemblies -> Add Assemblies...). Here are some screenshots of the application:

MbUnit GUI

MbUnit report

Conclusion

This article has presented composite unit testing, a new strategy for designing and implementing unit testing. Awaiting comments. :)

Reference

History

  • 17th May, 2004: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Engineer
United States United States
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Comments and Discussions

 
QuestionAnybody ever reads these topics? Pin
Member 152007714-Dec-04 3:19
Member 152007714-Dec-04 3:19 
AnswerRe: Anybody ever reads these topics? Pin
Marc Stober21-Dec-06 4:29
Marc Stober21-Dec-06 4:29 
Generaltypefixture parameter problem Pin
Member 152007716-Nov-04 2:40
Member 152007716-Nov-04 2:40 
GeneralI had the same problem with MbUnit 2.22 Pin
tim73di4-Apr-05 7:18
tim73di4-Apr-05 7:18 
AnswerRe: I had the same problem with MbUnit 2.22 Pin
LuckyJames15-Aug-06 4:04
LuckyJames15-Aug-06 4:04 
QuestionHow does this test all members? Pin
Member 55837530-May-04 14:27
Member 55837530-May-04 14:27 
AnswerRe: How does this test all members? Pin
Jonathan de Halleux1-Jun-04 23:42
Jonathan de Halleux1-Jun-04 23:42 
GeneralRe: How does this test all members? Pin
Member 5583752-Jun-04 14:09
Member 5583752-Jun-04 14:09 
GeneralRe: How does this test all members? Pin
Jonathan de Halleux2-Jun-04 21:09
Jonathan de Halleux2-Jun-04 21:09 
GeneralInteresting article. Pin
Qwertie25627-May-04 12:29
Qwertie25627-May-04 12:29 
GeneralRe: Interesting article. Pin
Jonathan de Halleux27-May-04 15:29
Jonathan de Halleux27-May-04 15:29 
Generalintegration sharpdevelop Pin
Tom Janssens17-May-04 21:55
Tom Janssens17-May-04 21:55 
GeneralRe: integration sharpdevelop Pin
Jonathan de Halleux17-May-04 22:33
Jonathan de Halleux17-May-04 22:33 
GeneralRe: integration sharpdevelop Pin
Tom Janssens18-May-04 0:07
Tom Janssens18-May-04 0:07 
GeneralRe: integration sharpdevelop Pin
Jonathan de Halleux18-May-04 0:21
Jonathan de Halleux18-May-04 0:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.