Friday, May 26, 2006

More and More MbUnit Goodness

I’ve been fooling around with MbUnit off and on for several months, and I continue to be knocked over by its goodness and killer features.

One cool thing I’m working on today is the TypeFixture attribute/test fixture which lets you use factories to bounce different instances through one set of tests.  This works very nicely for testing different types implemeting a common interface.

For example, here’s a simple (ok, contrived) example using an interface defined for employee object functionality:

public interface IEmployee

{

    string FName { get; set; } 

    string LName { get; set; } 

    double HourlyRate { get; set; } 

    string WageType { get; set; }

}

If I’ve SalariedEmployee and HourlyEmployee classes which both implement this, then I can do some quick testing by first building a couple factories:

public class EmployeeFactory

{

    [Factory]

    public SalariedEmployee BasicSalaried

    {

        get

        {

            return new SalariedEmployee("FirstName",

                                    "LastName",

                                    50,

                                    Employee.WageTypes.SALARY);

        }

    }

 

    [Factory]

    public SalariedEmployee BasicHourly

    {

        get

        {

            return new SalariedEmployee("FirstName",

                                    "LastName",

                                    50,

                                    Employee.WageTypes.HOURLY);

        }

    }

 

}

Now I create another class marked as a TypeFixture and let that class know where it will get its data from (the EmployeeFactory class above), and of what type that data will be (objects implemeting IEmployee).  This TypeFixture will deal with IEmployee objects as defined in the attribute.

[TypeFixture(typeof(IEmployee))]

[ProviderFactory(typeof(EmployeeFactory), typeof(IEmployee))]

public class MBIEmployeeTests

{

    [Test]

    [ExpectedException(typeof(ArgumentOutOfRangeException), "Rate less than zero")]

     public void CheckRateFailsUnderZero(IEmployee emp)

    {

        emp.HourlyRate = -20;

    }

 

    [Test]

    [ExpectedException(typeof(ArgumentOutOfRangeException), "Invalid wage type!")]

     public void CheckBadWageTypeFails(IEmployee emp)

    {

        emp.WageType = "Bogus!";

    }

 

 

}

MbUnit will run both the types output by the Factory-marked methods through the test fixture, resulting in four different tests.  Here’s the output from TestDriven’s test runner:

------ Test started: Assembly: JHSCR.Tests.dll ------

Test Execution

Exploring JHSCR.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

MbUnit 1.0.2232.25376 Addin

Found 4 tests

[success] MBIEmployeeTests.EmployeeFactory.BasicSalaried.CheckRateFailsUnderZero

[success] MBIEmployeeTests.EmployeeFactory.BasicSalaried.CheckBadWageTypeFails

[success] MBIEmployeeTests.EmployeeFactory.BasicHourly.CheckRateFailsUnderZero

[success] MBIEmployeeTests.EmployeeFactory.BasicHourly.CheckBadWageTypeFails

[reports] generating HTML report

TestResults: file:///D:/Data/Iterative%20Rose/Presentations/Test%20Tools/JHSCR/Build/JHSCR.Tests.Tests.html

4 passed, 0 failed, 0 skipped, took 2.41 seconds.

Nifty stuff.  Elegant implementation and a big timesaver.

3 comments:

Haacked said...

I love that feature. I don't use it nearly enough. I wrote about this a while back along with some other fun features.

I use RowTest ALOT!

http://haacked.com/archive/2005/10/18/SwitchingToMbUnit.aspx

Jim Holmes said...

Yep, I've read that post and several others you've written on MbUnit.

Good stuff!

I just wish that all the links on the MbUnit site to Peli's blog didn't go off to some error-ridden black hole. There's a lot of interesting looking topics which have fallen off into some other dimension.

Haacked said...

Now what I need to figure out is how to have the factories provider more than one object.

For example, i want to write a type fixture to test all my custom collections.

So it'd be nice to be able to have providers specify two objects instead of one. That way each test method might get both the collection to test and the object to add to the collection.

Of course i can create an intermediate holder object to do just that.

Subscribe (RSS)

The Leadership Journey