2011/03/01

Extending MbUnit with custom attributes

In a recent post, Krzysztof Kozmic discussed about an efficient way to link a particular test to an issue in a bug tracker. MbUnit provides several useful metadata attributes such as [Category], [Author], [TestsOn] etc. But none is about issue tracking :( Fortunately, the Gallio framework is easily extensible (did I say that already?); let's create such an attribute.

Custom Metadata Attribute

We will first create a new simple metadata attribute. We just need to derive from Gallio.Framework.Pattern.MetadataPatternAttribute and to implement some basic code logic in it.
[AttributeUsage(PatternAttributeTargets.TestComponent, AllowMultiple = true, Inherited = true)]
public class IssueAttribute : MetadataPatternAttribute
{
private readonly int issue;

public IssueAttribute(int issue)
{
this.issue = issue;
}

public int Issue
{
get { return issue; }
}

protected override IEnumerable<KeyValuePair<string, string>> GetMetadata()
{
yield return new KeyValuePair<string, string>("Issue", issue.ToString());
}
}
We can now use that simple attribute and enjoy the information displayed in the test report.
[TestFixture]
public class MyTestFixture
{
[Test, Issue(123456)]
public void MyTest()
{
}
}

Custom Test Decorator

But wait! We can certainly do better. My favorite issue tracker is a fancy web application and I would like to get an hyperlink that leads me directly to that issue. We cannot use Gallio metadata for that purpose because they are only key/value pairs of strings. Therefore it's uneasy to make them hold more interesting data like an URL. We will create a new test decorator instead; and use the powerful test log API to display cool stuff in the report. Let's implement a new test decorator attribute (MbUnit.Framework.TestDecoratorAttribute)
[AttributeUsage(PatternAttributeTargets.Test, AllowMultiple = true, Inherited = true)]
public class IssueAttribute : TestDecoratorAttribute
{
private readonly int issue;

public IssueAttribute(int issue)
{
this.issue = issue;
}

public int Issue
{
get { return issue; }
}

protected override void Execute(PatternTestInstanceState testInstanceState)
{
using (TestLog.BeginSection("Issue"))
{
TestLog.Write("This test is related to the ");
using (TestLog.BeginMarker(Marker.Link("http://MyCoolBugTracker/Issue/" + issue)))
{
TestLog.WriteLine("issue #" + issue);
}
}

base.Execute(testInstanceState);
}
}
Look now at the cool link printed in the test report: