MbUnit, my favorite xUnit Framework, is easy to extend. This tutorial will cover creating a new type of test fixture attribute and test method attribute. There are basically two reasons for creating extensions: to solve a general need and solve a specific need. The need to create an extension for a specific context happens far more often so we will focus on it. Marc Stober’s comment puts it well “…some of what makes MbUnit popular is that you can use the same framework for integration testing…A lot of us like MbUnit because it just gives us a way to test whatever we think needs testing”. The example I will use in this tutorial will be for an integration test, not a unit test.

First lets get you introduced to the participant types:

TestFixturePatternAttribute

IRun

TestPatternAttribute

MethodRunInvoker

The first two are for a fixture and the second two are for a method. Test fixture attributes need to inherit from TestFixturePatternAttribute. This subtyping is how MbUnit finds test fixtures, it looks for methods that are decorated with all attributes deriving from TestFixturePatternAttribute. The secondary function of the type is to provide the type of IRun to be used, a factory method. It is important to understand that IRun is a decorator, as in the Decorator Pattern. This is how setup and teardown are added to chain of execution.

Our subtype of IRun’s main responsibility is to populate the execution tree with IRunInvokers, in our case MethodRunInvokers. It will add to the name of each test being reported as well through the readonly property Name.

The TestPatternAttribute is the base type of the familiar TestAttribute, the one your are used to decorating most if not all your test methods with. This attribute is important, reflection will be used our IRun locating each method decorated with this attribute. For each decoration a MethodRunInvoker will be created. This is the glue between our IRun and MethodRunInvoker.

Our subtype of MethodRunInvoker’s main responsibility is to execute the test method. This is were you will tie it all together. You can add behavior before, after, and during. One of the other important responsibilities of this type is to provide a name for the test. This can be as simple as the name of the decorated method or more complex.

Now that you have some idea about the players lets get into the problem we will solve. We are going to create a commandline utility that takes the path of an xHtml file or a directory path and operates on all the xHtml files in the directory. It will validate the format complies with the dtd referenced by the file. It will produce an html report and display it in a browser.

Besides the normal attribute stuff notice the override GetRun. This is were the sequence of functions that will make up the test is created: SetUp, Test, TearDown. Instead of the normal test an XHTMLFixtureRun is created.

public class XHtmlFixtureRun : IRun

{

#region IRun Members

public bool IsTest

{

get { return false; }

}

public string Name

{

get

{

return “XHtmlFixture”;

}

}

public void Reflect(

MbUnit.Core.Invokers.RunInvokerTree tree,

MbUnit.Core.Invokers.RunInvokerVertex parent,

Type subjectType)

{

FileInfo[] Files = null;

if (XHtmlFileTester.File != null)

{

Files = new FileInfo[1]

{ new FileInfo(XHtmlFileTester.File) };

}

else if (XHtmlFileTester.Directory != null)

{

Files = new DirectoryInfo(XHtmlFileTester.Directory)

.GetFiles(“*.html”);

}

MethodInfo ValidateTest = TypeHelper.GetAttributedMethod(

subjectType, typeof(XHtmlFileAttribute));

foreach (FileInfo XHtmlFile in Files)

{

Object[] Args = new Object[1] { XHtmlFile };

String TestName = String.Format(“File {0}”,

XHtmlFile.Name);

IRunInvoker TestInvoker = new MultiParameterRunInvoker(

this, ValidateTest, Args, TestName);

TestInvoker = DecoratorPatternAttribute.DecoreInvoker(

ValidateTest, TestInvoker);

tree.AddChild(parent, TestInvoker);

}

}

#endregion

}

This guys main responsibility is expressed in the Reflect method, as mentioned earlier. We are only going to place one test method attribute on our test fixture so we only look for one. You could search for more than one and add to the tree for each that you find. Call GetAttributeMethods, plural, for such a situation. Here thou we will be add to the tree for each file that we find. Note that the name of the test will include the name of the file. We also want to pass that file to the test method when it is being executed. That is were the MultiParameterRunInvoker comes in.

public class MultiParameterRunInvoker : MethodRunInvoker

{

private String _Name;

private Object[] _Args;

public Object[] Args

{

get

{

return _Args;

}

set

{

_Args = value;

}

}

public override string Name

{

get

{

return this._Name;

}

}

public MultiParameterRunInvoker(

IRun generator,

MethodInfo methodInformation,

Object[] args, String testName)

: base(generator, methodInformation)

{

this._Name = testName;

this.Args = args;

}

public override object Execute(object o, IList args)

{

foreach (Object Arg in this.Args)

{

args.Add(Arg);

}

return base.Execute(o, args);

}

}

There is not much to this one. We store the MethodInfo and the arguments to pass to it and execute it when Execute is called.

Before we get to far away from the XHtmlFixtureRun.Reflect: the one test method attribute that we looked for. It is very simple:

[AttributeUsage(

AttributeTargets.Method,

AllowMultiple = false,

Inherited = true)

]

public class XHtmlFileAttribute : Attribute

{

}

It is just a method attribute type and that is it. So we have all our extension pieces, lets use them. Here is the test fixture:

To begin notice that the class is decorated with the attribute XHtmlFixture. Next skip on down to the FileTester method. It to is decorated with one of our new custom methods: XHtmlFile. It will create an xml reader, configure it for validation, and read the file. The validator events when there is a violation exception. This event is handled by the method TestValidationEventHandler. It will set a flag and store the violation exception. If a violation occurs the reading will stop. After the reading has stop, for a violation or EOF the flag will be asserted on. So if there are any errors the assertion will fail. Now we just need to get files pumped into this rig.

I you look back at the method XHtmlFixtureRun.Reflect you will notice that it is calling on the static properties Directory and File of the XHtmlFileTester fixture that we just examined. These static properties are how we are going to pump the files in, we need to get them from the commandline. I like to use Param.NET and you can read about it on the Code Project if you like. It will allow us to keep sub main nice and clean:

static class Program

{

static int Main(string[] args)

{

ParamCollection Parameters = ParamCollection.ApplicationParameters;

CommandLineOptions Options = new CommandLineOptions();

Options.Directory = (string)Parameters[“Directory”].Value;

Options.File = (string)Parameters[“File”].Value;

if (Options.File == null && Options.Directory == null)

{

System.Windows.Forms.MessageBox.Show(

“No file or directory specified!”,

“A little help please!”);

return -1;

}

XHtmlFileTester.Directory = Options.Directory;

XHtmlFileTester.File = Options.File;

using (AutoRunner runner = new AutoRunner())

{

runner.Load();

runner.Run();

runner.ReportToHtml();

return runner.ExitCode;

}

}

}

Notice how we populate the static properties XHtmlFileTester.Directory and XHtmlFileTester.File. I am sure that you also see the AutoRunner. That is a MbUnit class. It will load the fixture XHtmlFileTester, execute it, create a report, and present it. Very easy! All that is left is to compile and execute. In the example project you will find an install.reg file. Edit the paths to reflect the location where you compiled to. This reg file will add a menu item to the context menu for directories in Windows Explorer. Right click the web directory in example project and select Validate xHtml. A web browser should open with a report showing one passing file and one failing file. This is pretty cool and all but if you know the MbUnit fixture attributes I am sure you are thinking that this could have been more easily coded with the TestSuiteFixture. Your right, but we aren’t done yet.

But wait there’s more!

It would be really nice to know how many violations there are in a non-compliant file. Lets change it so that every validation reports as a failure. To do this we will need to actually do the validation in the XHtmlFixtureRun. Every time the validation event is fired we will add to the tree object a test method that does nothing but call Assert.Fail. If we make it to the end of the file with out violation we will add a test method the does nothing, it will pass. Here are altered XHtmlFixtureRun and XHtmlFileTester.

This turns things around a bit. The unit test execution normally happens in the test fixture but here the test fixture is used just to relay the results. The design of MbUnit is very flexible. I would say that using it in this way is beyond its intended use but it works well. Now when we execute we get a report that shows more than just one failing test and one passing test. We get one passing test and 5 failing tests: one failure for each violation. One key thing to take not of is that the while loop reading the file no longer looks to see if the file has proven to be in valid and stop reading if so. This allows the reading to continue to the end of the file collecting all the violations. Below is the report out. Notice the names of the tests. The first test name is XHtmlFileTester.File bad.html Line 10 Position 2. The first part of the name is contributed by XHtmlFixtureRun.Name. The second part is made in XHtmlFixtureRun.Reflect but is provided to the MbUnit framework by MultiParameterRunInvoker.Name. So the format for the name is <XHtmlFixtureRun.Name>.<MultiParameterRunInvoker.Name>.

When I asked the MbUnit Google Groups to review this tutorial Peli (the creator of MbUnit) suggested that I make the extension reusable. This got me thinking that you, as in you the reader, could take the code provided in this tutorial and alter it to test any xml against any dtd.

[XmlValidationFixtureAtttribute(

XmlFile=@”path\document.xml”,

DtdFile=@”path\document.dtd”)]

public class MyXmlTester : XmlValidationFixture

{

}

You could then use this in any project that included xml files that must conform to a dtd specification. You could also use it in projects that produce xml that must conform with a dtd. I am sure that you get the idea, I will leave you to it.

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.