Showing posts with label Extensibility. Show all posts
Showing posts with label Extensibility. Show all posts

2011/06/15

Extending MbUnit With Custom Expected Exception Attributes

MbUnit provides several ways to deal with expected exceptions in the user code under test. The most popular one is certainly to decorate the test method with an [ExpectedException] attribute:
[Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Constructs_Foo_with_negative_value_should_throw_exception()
{
new Foo(-123);
}
Conveniently, you can use some built-in attributes to save a few more keystrokes:
[Test, ExpectedArgumentOutOfRangeException]
public void Constructs_Foo_with_negative_value_should_throw_exception()
{
new Foo(-123);
}
Those shortcut attributes are very useful. They significantly improve the readability of the tests by removing two pairs of noisy nested parenthesis.

But what if you want to define your own shortcut attribute for a custom exception of yours? Imagine for example that you use a fancy SpaceTimeBrokenException all over your code. Let's define a custom shortcut expected exception attribute for it. That's very easy with Gallio's extensibility model: you just need to derive from ExceptedExceptionAttribute like this:
using Gallio.Framework.Pattern;
using MbUnit.Framework;

[AttributeUsage(PatternAttributeTargets.Test, AllowMultiple = false, Inherited = true)]
public class ExpectedSpaceTimeBrokenExceptionAttribute : ExpectedExceptionAttribute
{
public ExpectedSpaceTimeBrokenExceptionAttribute()
: base(typeof(SpaceTimeBrokenException))
{
}

public ExpectedSpaceTimeBrokenExceptionAttribute(string message)
: base(typeof(SpaceTimeBrokenException), message)
{
}
}
That's all! Now just put that new class in a namespace accessible from your test project and enjoy it...
[Test, ExpectedSpaceTimeBrokenException]
public void Run_TimeMachine_with_negative_power_should_collapse_the_entire_universe()
{
var timeMachine = new TimeMachine();
timeMachine.Run(-1E10);
}

2011/05/10

More About Custom Data Source Attributes

Some weeks ago, I explained how to extend Gallio/MbUnit with custom data source attributes. The principle is to provide convenient and reusable attributes which feed test methods with data coming from an external source (e.g. file, database, distant service, etc.)

Aleksandr Jones has just written a very nice article in his blog that illustrates the concept. He explains how to create an attribute that grabs test parameters from an OLE/SQL database. The typical usage is the following:
[Test]
[CustomAttribute("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=MikeGarage.accdb;Persist Security Info=False;", "Customers")]
public void CustomAttributeTest(DataRow dataRow)
{
// ...
}

2010/10/07

Testing Custom Data Source Attributes in MbUnit v3.2

In a recent post, I explained how to create a custom data source attribute for MbUnit. But before using it, it's certainly safer to test it. The method described below is the same as the one which is applied in the MbUnit test project itself. It is widely used to verify that the built-in attributes of MbUnit behave as expected. The principle is the following:
  1. Create a nested explicit sample test fixture which consumes the attribute under test. It must be marked as explicit, so that it will not be taken in account by the primary test runner.
  2. Create a regular unit test which launches an inner isolated test runner, and runs the sample fixture.
  3. Retrieve the output of the inner test runner and assert over the test log.
The Gallio framework has everything you need to create an isolated test runner. But in order to make the things easier, the Gallio SDK (you can find the SDK under %gallio_install_path%\sdk) contains a couple of handy helper classes for that very purpose. It provides in particular a BaseTestWithSampleRunner class that you can use as a base class of you main fixture, and a [RunSample] attribute to easily target the nested explicit sample fixtures.

Here is a simple example that shows how to test the [BooleanData] attribute that we did create last time.
[TestFixture, RunSample(typeof(SampleFixture))]
public class BooleanDataAttributeTest : BaseTestWithSampleRunner
{
[Test]
public void Test()
{
var runs = GetTestStepRuns(typeof(SampleFixture), "Test");
var logs = runs.Select(GetLog).Where(x => x.Length > 0);
Assert.AreElementsEqualIgnoringOrder(new[] { "value=True", "value=False" }, logs);
}

[TestFixture, Explicit]
internal class SampleFixture
{
[Test]
public void Test([BooleanData] bool value)
{
TestLog.Write("value={0}", value);
}
}
}
Want to know more? Be sure to read this page in the Gallio wiki.

2010/09/03

Writing Custom Data Source Attributes in MbUnit v3.2

The extensibility of the Gallio platform is simply amazing. You can virtually extend any part of the system. It goes from a simple plugin that provides new handy functionalities to a full-blown adapter for your fancy testing framework. Today, I would like to explain how easy it is to extend the MbUnit framework with a custom data source attribute.

MbUnit has many useful built-in data source attributes which might be used to create powerful data-driven tests. The most popular attributes are certainly [Row] and [Column].

Now let's imagine you get bored with writing that kind of tests:
[Test]
public void Mytest(
[Column(true, false)] bool flag1,
[Column(true, false)] bool flag2,
[Column(true, false)] bool flag3)
{
// ...
}
Imagine how beautiful would be the world if you could write the following instead:
[Test]
public void Mytest(
[BooleanData] bool flag1,
[BooleanData] bool flag2,
[BooleanData] bool flag3)
{
// ...
}
Unfortunately, this data source attribute does not exist in MbUnit.

So let's make it happen!

Creating a custom data source is very easy. Basically you simply need to derive from MbUnit.Framework.DataAttribute, and to override the virtual method PopulateDataSource.
[AttributeUsage(PatternAttributeTargets.DataContext, AllowMultiple = false, Inherited = true)]
public class BooleanDataAttribute : DataAttribute
{
protected override void PopulateDataSource(IPatternScope scope, DataSource dataSource, ICodeElementInfo codeElement)
{
dataSource.AddDataSet(new ValueSequenceDataSet(new object[] { true, false }, GetMetadata(), false));
}
}
That's all. Compile and run your test happily!

You might find more inspiration by examining the actual implementation of existing built-in attributes such as [EnumData] or [RandomStrings] which is slightly more complicated as it relies on the underlying Gallio data generation framework.

Next time, I will explain how to properly test you custom data source attribute by using the Gallio SDK.