|
MbUnit : ExtendingMbUnitWithYourTestDecorator
This page last changed on Jul 02, 2005 by peli.
MbUnit supports a system of DynamicProxy that can be attached to tests. Such 'strange thing' is also called a TestDecorator because it "decorates" the way the test is executed. The most famous example of test decorated is ExpectedExceptionAttribute which verifies that a method throws the right exception. ExampleLet us illustrate this with an example. Consider a decorator that sets an environment variable value and cleans up once the test is done. So, for example, we would like to write tests like this: Unable to find source-code formatter for language: cs. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml [TestFixture] public class MyFixture { [Test] [PushEnvironmentVariable("Path","")] public void RunTestWithoutPath() { ... } } In this example, we expect that the Path variable is set to "" for the test and restore afterwards. Creating a new decoratorCreating a new decorator involves 2 steps:
When MbUnit loads the tests, it looks for any DecoratePatternAttribute. This class defines an abstract method that installs the dynamic proxy: Unable to find source-code formatter for language: cs. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml public abstract IRunInvoker GetDecorator(IRunInvoker invoker); IRunInvoker is an interface that defines an executable instance. For instance, it can be a wrapper around a MethodInfo invokation. This interface defines an Execute method that is invoked by MbUnit: Unable to find source-code formatter for language: cs. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml public Object Execute(Object fixture, IList args); The GetDecorator method is usually quite short because we just install the proxy (step 2) which contains the real interresting code. The skeleton of the application looks like this: Unable to find source-code formatter for language: cs. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml namespace MbUnit.Framework
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited =true)]
public sealed class PushEnvironmentVariableAttribute : DecoratorPatternAttribute
{
...
public override IRunInvoker GetInvoker(IRunInvoker invoker)
{
return new PushEnvironmentVariableRunInvoker(invoker, this);
}
private sealed class PushEnvironmentVariableRunInvoker : DecoratorRunInvoker
{
private PushEnvironmentVariableAttribute attribute;
public PushEnvironmentVariableRunInvoker(
IRunInvoker invoker,
PushEnvironmentVariableAttribute attribute
)
:base(invoker)
{
this.attribute = attribute;
}
public override Object Execute(Object o, IList args)
{
// pre actions
...
// invoke wrapped invoker
this.Invoker.Execute(o,args);
// post actions
...
}
}
}
}
Of course, we still have to write a lot of gaps but the rest is detail (full implementation is given below). Example sourceUnable to find source-code formatter for language: cs. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml using System; using System.Collections; using MbUnit.Core.Framework; using MbUnit.Core.Invokers; namespace MbUnit.Framework { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited =true)] public sealed class PushEnvironmentVariableAttribute : DecoratorPatternAttribute { private string name; private string value; public PushEnvironmentVariableAttribute( string name, string value ) { this.name = name; this.value = value; } public string Name { get { return this.name; } } public string Value { get { return this.value; } } public override IRunInvoker GetInvoker(IRunInvoker invoker) { return new PushEnvironmentVariableRunInvoker(invoker, this); } private sealed class PushEnvironmentVariableRunInvoker : DecoratorRunInvoker { private PushEnvironmentVariableAttribute attribute; public PushEnvironmentVariableRunInvoker( IRunInvoker invoker, PushEnvironmentVariableAttribute attribute ) :base(invoker) { this.attribute = attribute; } public override Object Execute(Object o, IList args) { // store previous value string previousValue = Environment.GetEnvironmentVariable( attribute.Name ); try { // install new value Environment.SetEnvironmentVariable( attribute.Name, attribute.Value ); // run base invoker return this.Invoker.Execute(o, args); } finally { // cleaning our mess Environment.SetEnvironmentVariable( attribute.Name, previousValue ); } } } } } |
| Document generated by Confluence on Jun 11, 2007 11:56 |