Andrew Stopford's Weblog

poobah

Sponsors

News

Articles

Family

Old Blogs

MbUnit TypeFixture

This is the first of series of posts I'll be running on MbUnit, I'll focus on v2 first and then show an example in v3. I won't focus on RowTest as this is a popular feature and is well known both by the MbUnit audience and by other frameworks. For the opening post I'll focus on the TypeFixture, let's take a look at the code.

   1:  using System;
   2:  using System.Collections;
   3:  using MbUnit.Framework;
   4:   
   5:  [TypeFixture(typeof(IEnumerable))]
   6:  public class TypeFixtureExample
   7:  {
   8:      [Provider(typeof(IEnumerable))]
   9:      public ArrayList ProviderEmptyArrayList()
  10:      {
  11:          return new ArrayList();
  12:      }
  13:      
  14:      [Provider(typeof(IEnumerable))]
  15:      public ArrayList ProviderArrayList()
  16:      {
  17:          ArrayList list = new ArrayList();
  18:          list.Add(0);
  19:          list.Add(1);
  20:          return list;
  21:      }
  22:   
  23:      [Test]
  24:      [ExpectedException(typeof(InvalidOperationException))]
  25:      public void CurrentCalledBeforeMoveNext(IEnumerable en)
  26:      {
  27:            IEnumerator  er = en.GetEnumerator(); 
  28:            object p = er.Current;
  29:      }
  30:   
  31:      [Test]
  32:      [ExpectedException(typeof(InvalidOperationException))]
  33:      public void CurrentCalledAfterFinishedMoveNext(IEnumerable en)
  34:      {
  35:          IEnumerator er = en.GetEnumerator();
  36:          while (er.MoveNext());
  37:          object p = er.Current;
  38:      }
  39:  }

We have two tests that will run on both methods that are decorated by the Provider attribute, each provider set's up an ArrayList as our target type and each test runs against both providers. Each provider seeds the test method, you could also define one provider as your target type and any number of tests against that provider. If you want to break up your tests into reusable parts then you could do the following.

   1:  using System.Collections;
   2:  using MbUnit.Framework;
   3:   
   4:  public class ArrayListFactory
   5:  {
   6:      [Factory]
   7:      public ArrayList ProviderEmptyArrayList
   8:      {
   9:          get { return new ArrayList(); }
  10:      }
  11:   
  12:      [Factory]
  13:      public ArrayList ProviderArrayList
  14:      {
  15:          get
  16:          {
  17:              ArrayList list = new ArrayList();
  18:              list.Add(0);
  19:              list.Add(1);
  20:              return list;
  21:          }
  22:      }
  23:  }

and use these as follows

   1:  using System;
   2:  using System.Collections;
   3:  using MbUnit.Framework;
   4:   
   5:  [TypeFixture(typeof(IEnumerable))]
   6:  [ProviderFactory(typeof(ArrayListFactory), typeof(IEnumerable))]
   7:  public class ProviderFactoryTest
   8:  {
   9:      [Test]
  10:      [ExpectedException(typeof(InvalidOperationException))]
  11:      public void CurrentCalledBeforeMoveNext(IEnumerable en)
  12:      {
  13:          IEnumerator er = en.GetEnumerator();
  14:          object p = er.Current;
  15:      }
  16:   
  17:      [Test]
  18:      [ExpectedException(typeof(InvalidOperationException))]
  19:      public void CurrentCalledAfterFinishedMoveNext(IEnumerable en)
  20:      {
  21:          IEnumerator er = en.GetEnumerator();
  22:          while (er.MoveNext()) ;
  23:          object p = er.Current;
  24:      }
  25:  }

Here we break out the providers into Factories and referance them in our test using the ProviderFactory attribute. So unlike our first test the Providers are isolated from the test and reusable across other tests however we can still seed our tests as before.

Let's take a look at how MbUnit v3 does this.

   1:  using System.Collections.Generic;
   2:  using MbUnit.Framework;
   3:   
   4:  public class TypeFixtureExample
   5:  {
   6:      public static IEnumerable<IEnumerable> GetInstances()
   7:      {
   8:          yield return new ArrayList();
   9:          yield return new ArrayList { 0, 1 };
  10:      }
  11:   
  12:      [Factory("GetInstances")]
  13:      public IEnumerable Instance;
  14:   
  15:      [Test]
  16:      [ExpectedException(typeof(InvalidOperationException))]
  17:      public void CurrentCalledBeforeMoveNext()
  18:      {
  19:          var er = Instance.GetEnumerator();
  20:          var p = er.Current;
  21:      }
  22:   
  23:      [Test]
  24:      [ExpectedException(typeof(InvalidOperationException))]
  25:      public void CurrentCalledAfterFinishedMoveNext()
  26:      {
  27:          var er = Instance.GetEnumerator();
  28:          while (er.MoveNext()) ;
  29:          var p = er.Current;
  30:      }
  31:  } 

V3 has done away with the Provider attribute, we use Factory attributes as before but no longer need the ProviderFactory attribute as v3 is a little cleverer at wiring up tthe data.  Our type tester methods remain the same however we seed them using the Factory attribute, this like the v2 attribute will create and hold test data for us. In this example the Factory attribute creates a enumerable list of our type along with seeded data. The GetInstances method plays the role of the Provider methods in the v2 example, we are using some 3.5 sugar to shorten the syntax here but the principle of seeded types remains the same.

Posted: Aug 25 2008, 08:41 PM by andrewstopford | with 4 comment(s)
Filed under: ,

Comments

Jeremy Gray said:

Excellent stuff, indeed, which could only be even more excellent through the removal of that "GetInstances" magic string you have there. I know, I know, good refactoring tools can see the string too, but I'm sure that you still feel a bit of a twinge, as I do, when seeing that magic string. ;)

# August 25, 2008 10:37 PM

Jeff Brown said:

Since the attribute appears at point of use, we can't get rid of the string because there may be multiple factories so we have to specify which one to use.

In general, it's not possible to guess which method is the factory method.  Moreover, a factory might be defined on a different type (as a static method).

That said, if you rename the factory (uncommon), then the tests will fail... which should serve as a good enough reminder the moment you run your freshly refactored test.  ;-)

It's also important to note that there are multiple ways of specifying these tests.  The [Factory] could have appeared on the test method itself, for example, assuming the test method had a parameter of the right type.  It could also have appeared on the fixture's constructor.  It could also have been given a data source name and referenced indirectly from the tests, and so on.

So you're free to decide your own conventions based on the available syntax.

# August 26, 2008 3:33 PM

Community Blogs said:

MbUnit 3 has been a long, long labour. It started around early fall of 2007 and under Jeff had grown

# April 2, 2009 6:26 PM

MSpec – Take 2 | Elegant Code said:

Pingback from  MSpec &ndash; Take 2 | Elegant Code

# July 5, 2009 3:38 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)