Testing Times Ahead: Extending NUnit

14 March 2008
by Ben Hall

If you want to get serious with Unit Testing, then you'll need to understand how to extend the NUnit framework in different ways  for your own particular test requirements and how to tailor test behaviour. Test Expert Ben Hall, of the SQL Data Generator team, shows how it is done, with special reference to the art of testing SQL code.

Extending NUnit 2.4

This article discusses why you might want to extend NUnit with custom addins and attributes, and how to go about doing it.  NUnit has become the de-facto standard unit-testing framework for the .Net platform.  Originally a port of JUnit, it has now grown, and has been completely re-written to take advantage of the .Net framework.   This article is based on NUnit 2.4.6.

The sample source code for this article can be downloaded here.

Why Extend?

Why would you need to extend NUnit?  NUnit provides an excellent harness for unit testing, but you will have requirements which cannot be solved using the framework. 

By extending NUnit, you can move this generic repeatable code from your test code into an attribute that can then be added to your test methods.  

This has a number of advantages:

  • Your tests are more readable because an attribute can describe more about the test than some code in a setup method,
  • You can reuse your extensions instead of them being isolated in test code.  
  • You can also extend the code to allow additional possibilities not possible out of the box, such as dynamically creating your tests and test data.

NUnit Extension Points

NUnit 2.4 has a set of extension points that allow you to hook into the framework at various different levels to solve different problems. 

These are the different types of extension points that are available.

1)      Suite Builders

This is arguable the most flexible and highest level hook into NUnit. The Suite Builder extension point allows you to implement your own version of TestFixture.  This means you can redefine much of the way that NUnit finds, and executes the tests.  For example, if you need a different way to identify tests, instead using the [Test] attribute, you could create your own Suite Builder and, by using Reflection on the type, you could dynamically add methods as tests.

 

Another advantage of this is that you can define the way to execute the Test Method.  Every test is defined as a Test object, these Test objects define the tests name, how the test should be executed, the arguments for a test, any expected exceptions and other information NUnit uses internally.  By dynamically creating your test fixture, you can alter the behaviour of any of the tests. 

 

2)      Test Case Builders

Where Suite builders work by redefining the TestFixture, Test Case Builders redefine how the Test attribute behaves.  By using the Test Case Builder to hook into NUnit, you can define a custom test within a standard NUnitTestFixture

 

These custom tests work by dynamically creating the test objects and adding them to an existing Test Fixture to execute.  This allows you to dynamically build the tests to execute within your test fixture based on data that might not be known until run time, for example the contents of a directory or database.

 

3)      Test Decorators

Test decorators are combined with an existing [Test] attribute in order to add additional behaviour to a test when it is executed, such as modifying the description of the test or executing a command before the test is started.  Unlike the two previous tests, we still use NUnit’s basic features but tweak the actual execution.

 

An example of this is being able to add a RepeatAttribute to a test which causes the test to execute a certain number of times instead of just once.

 

4)      Event Listeners

The fourth main way to extend NUnit is to listen to the various events Nunit fires during execution of the test suite.  The events you can hook into are:

·         Run Started

·         Run Finished

·         Test Started

·         Test Finished

·         Suite Started

·         Suite Finished

·         Unhandled Exception

·         Test Output

By listening to these events, you can add a response as required.  A common requirement for this would be to provide additional/different reporting functionality of test execution.

Note: Version 2.4.6 has a known bug with event listeners that cause the loading of the addin to fail.

How to Extend NUnit?

So how would you actually go about implementing the addins?

Hello World

To start with, I am going to create a Hello World attribute.  NUnit addins can be created as any .Net assembly as long as they reference the correct NUnit assemblies and implement the interfaces correctly. 

To get started, you will need to create a class library (I will be using C#), you will then need to reference the correct NUnit assemblies, which can be found in the nunit directory, which are nunit.framework.dll, nunit.core.dll and nunit.core.interfaces.dll.  With those in place, we can create our first addin.

The type of extension point and addin you wish to create will depend on how the addin gets implemented.  For this example, I will create a Test Case Builder addin that will take a test object and add a description before passing it back to NUnit to execute.  The Test Case Builder requires an object to implement the NUnit.Core.Extensibility.ITestCaseBuilder interface.

The interface has two method, one called CanBuildFrom which returns true if the current test method is supported by the attribute and false if it isn’t.  The second method is called BuildFrom that takes the current method as a MethodInfo object and returns a constructed Test object to execute. Figure 1 demonstrates the code required to implement the addin.  The CanBuildFrom just checks to make sure it has the correct attribute (see below), the BuildFrom method then simply creates the built-in NUnitTestMethod object based on the current method, then sets the description property and finally returns it to the calling code (NUnit).

#region ITestCaseBuilder Members

public bool CanBuildFrom(System.Reflection.MethodInfo method)

{

    return NUnit.Core.Reflect.HasAttribute(method, "NUnitAddInAttributes.HelloWorldAttribute", false);

}

 

public NUnit.Core.Test BuildFrom(System.Reflection.MethodInfo method)

{

    NUnitTestMethod tmethod = new NUnitTestMethod(method);

    tmethod.Description = "Hello World Test Addin Method";

    return tmethod;

}

#endregion


Figure 1: - ITestCaseBuilder code


The next stage is to create the correct attribute so it can be used by the test code.  The first time I attempted this, I put the attributes into the same assembly as the addins.  However, this meant that my test code also had to reference nunit.core and nunit.core.interfaces if it wanted to use my attribute: this is not great for ‘zero fiction’ usage, so I moved my attribute code into a separate assembly that my test code could then reference in order to use my addin.  This had the benefit that they don’t need to reference any additional assemblies.

As the attribute is used to tell NUnit to use the addin instead of NUnit’s Test attribute, the attribute itself is simple as shown in figure 1.

namespace NUnitAddInAttributes

{

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

    public class HelloWorldAttribute : Attribute

    {

    }

}

Figure 2: - Hello World Attribute


Now we have a useable addin, we need to tell NUnit about it.  The first step is to add a NUnitAddinAttribute to the addin class so that NUnit can find it.

[NUnitAddin(Description = "Hello World Plugin")]

public class HelloWorld : NUnit.Core.Extensibility.IAddin, ITestCaseBuilder

Figure 3: - NUnitAddinAttribute


The next step is to install it into the correct extension point.  The IAddin interface has a single method called Install; this has a IExtensionHost parameter that we can use to gain access to the different extension points.  By calling the method GetExtensionPoint and passing in the name of the extension we get a IExtensionPoint object.  We can then call the Install method on this object, passing in the object that implements the correct interface, finally returning true to say it completed successfully.

Figure 4 is the code for installing a Test Case Builder addin.

#region IAddin Members
public
bool Install(NUnit.Core.Extensibility.IExtensionHost host)

{

    IExtensionPoint testCaseBuilders = host.GetExtensionPoint("TestCaseBuilders");

    testCaseBuilders.Install(this); //this implments both interfaces

    return true;

}
#endregion


Figure 4: - Addin Install


If we load NUnit and go Tools > Addins the following dialog box will appear.

You should see the addin successfully listed, together with a RepeatedTestDecorator, which is included within NUnit.

Figure 5 is an example of how to use the attribute with your test.  Remember, a Test Case Builder addin attribute is used instead of the [Test] attribute which you would normally expect, so if we use the attribute there is no need to redefine it.

[HelloWorld]

public void Test()

{

    Assert.IsTrue(true);

}

Figure 5: - Usage Example


Within the NUnit GUI, in the properties dialog for the test, we can see that the description has successfully been set via the addin.

This is a very simple example, but shows how to create the Addin. Now for more detail.

Suite Builders

As discussed above, the suite builder allows you to redefine how the tests are found and executed. Addins that extend this will require the ISuiteBuilder interface.  This is similar to the ITestCaseBuilder interface, and has two methods – CanBuildFrom and BuildFrom.  However, instead of BuildFrom returning a single test, it returns a complete test suite with all the tests added.  The NUnit object model is slightly confusing where a TestSuite is actually a type of Test, which is why BuildFrom can return either single tests or a suite of tests.

public bool CanBuildFrom(Type type)

{

    return Reflect.HasAttribute(type, "NUnitAddinAttributes.SuiteBuilderAttribute", false);

}

 

public Test BuildFrom(Type type)

{

    return new SuiteTestBuilder(type);

}


Figure 6:- ISuiteBuilder code


With BuildFrom returning a TestSuite object, we need to implement the object and add any tests required.  Figure 7 is the code for my custom Test Suite.  Within the constructor, I use reflection to get a list of all the methods within the Type, which is the class containing all of the methods.  If the method name starts with MyTest, I add it to the TestSuite as a standard NUnitTestMethod, at this point I could also have redefined how each test is executed, I will discuss this in more detail in the next section.

public class SuiteTestBuilder : TestSuite

{

    public SuiteTestBuilder(Type fixtureType): base(fixtureType)

    {

        this.Fixture = Reflect.Construct(fixtureType);

        foreach (MethodInfo method in fixtureType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))

        {

            if (method.Name.StartsWith("MyTest"))

                this.Add(new NUnitTestMethod(method));

        }

    }

}

Figure 7: - Suite Test Builder code


With our TestSuite code completed, we need to install the addin.  To install the addin, we need to access the extension point called “SuiteBuilders” and pass in the ISuiteBuilder object as shown in figure 8.  We also need to add the NUnitAddinAttribute to the class.

public bool Install(IExtensionHost host)

{

    IExtensionPoint builders = host.GetExtensionPoint("SuiteBuilders");

    if (builders == null)

        return false;

 

    builders.Install(this);

    return true;

}


Figure 8: - Suite Builder Install


Finally, we need to implement an attribute for use within our tests, this is identify which classes should use the addin.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]

public class SuiteBuilderAttribute : Attribute

{

}

Figure 9: - Suite Builder Attribute


Figure 10 is an example of how to use the addin.  There is no reference to TestFixture or Test attributes, as we would expect. All we need to do is add the SuiteBuilderAttribute to the top of the class, then prefix any tests we want to be executed with ‘MyTest’, which is what the addin uses to identity the methods.

[SuiteBuilder]

public class SampleSuiteExtensionTests

{

    public void MyTest1()

    {

        Console.WriteLine("Hello from test 1");

    }

 

    public void MyTest2()

    {

        Console.WriteLine("Hello from test 2");

    }

 

    public void NotATest()

    {

        Console.WriteLine("This is not a test and so shouldn't be called.");

    }

}


Figure 10:- Usage Sample


The NUnit GUI should now show that we have two tests listed for that object which can be executed as normal.

This is a very powerful extension, but with limited usage. Test Case Builders and Test Decorates have much more of an impact.

Test Case Builders

Test Case Builders allow you to extend NUnit without having to rewrite large parts of the fundamental framework.  By using Test Case Builders, we can take a single method with the correct attribute and define one or more different tests that can be executed as part of the test suite.   This allows much more flexibility with NUnit, thereby improving the readability and maintenance of tests.  This is because the process of defining the tests to be executed can be customized via the attribute.  The example I will use to demonstrate this extension is the ability to define a test case for each row from a SQL statement, the columns of which are then passed into the test as parameters to use as part of the test. Figure 11 demonstrates the final extension being used.

Instead of defining the [Test] attribute, we define a SqlServerDataSource attribute, and set the connection string and query parameters. When NUnit loads the test, it will execute the query and create a separate test for each row returned from the query.

[SqlServerDataSource(ConnectionString = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True", Query = "SELECT CustomerID FROM Northwind.dbo.Customers")]

public void TestCustomerIDIsValid(string customerID)

{

    Console.WriteLine(customerID);

    Assert.IsFalse(string.IsNullOrEmpty(customerID));

}

Figure 11: - Sample Usage


When the tests are executed, it will pass in the values (one parameter for each column returned) so we can use them to test against.  This is useful for dynamically creating the tests, especially if you do not know all the possible test values when you are creating your tests. By using this attribute, anyone can include additional test cases for the method.

We have already created a Test Case Builder before in the Hello World example so I won’t go into the concept here.  Once we have inherited from the ITestCaseBuilder interface, we need to determine if tests can built from the method.

The major different is that our attribute now has properties which we can use to pass data into our attribute.  Within our attribute code, we simply add the properties we want.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

public class SqlServerDataSourceAttribute : Attribute

{

    public string ConnectionString { get; set; }

    public string Query { get; set; }

}

Figure 12: - Sql Server Data Source Attribute


Figure 13 defines if the test method is supported by the SqlDataSource Addin we are creating.  The first task is to see if the current method has a SqlServerDataSourceAttribute, this is done using the Reflect class which is a wrapper around System.Reflection and provides an interface that is easy to understand – this can be found in NUnit.Core.  If the method does not have the attribute then it is not supported so we return false.  Otherwise, we access the attribute using Reflect, and check to make sure the conversion works correctly and return true to indicate it is a supported method.

public bool CanBuildFrom(MethodInfo method)

{

    if (method == null)

        throw new ArgumentNullException("method");

 

    if (Reflect.HasAttribute(method, SqlServerAttribute, false))

    {

        SqlServerDataSourceAttribute sql = Reflect.GetAttribute(method, SqlServerAttribute, false) as SqlServerDataSourceAttribute;

        if (sql != null)

            return true;

    }

 

    return false;

}

Figure 13: - Sql Server Can Build From method


Once NUnit knows that it should use the Addin we need to define how it can actually build a test, or in this case a collection of tests.  Figure 14 shows the code for defining how to build the test suite.  The first task is to gain access to the attribute again, enabling us to access the connection string and query provided by the user.  We then need to define a standard TestSuite which is passed to the CreateRowsFromSQL method along with the method and the attribute (Figure 14), finally returning the populated suite back to NUnit. 

public Test BuildFrom(MethodInfo method)

{

    if (method == null)

        throw new ArgumentNullException("method");

 

    SqlServerDataSourceAttribute sql = Reflect.GetAttribute(method, SqlServerAttribute, false) as SqlServerDataSourceAttribute;

 

    string parentName = method.DeclaringType.ToString();

    TestSuite suite = new TestSuite(parentName, method.Name);

 

    CreateRowsFromSQL(method, suite, sql);

 

    return suite;

}

Figure 14: - Sql Server Build From code


Within CreateRowsFromSQL we create all of the tests to be executed and add them to the suite.  I am just using standard ADO.net to query the database based on the attribute’s properties.  Once I have queried the database, I iterate each field and use it to help create a test name which allows the test to be identified when executing.  Finally, I create a SqlServerTestMethod object that defines how the test is executed.  To setup the object; I pass in the test method, the name of the test and then the rows returned from the query as an object array for use during execution. I then add the created SqlServerTestMethod into the test suite for execution.

sqlConn = new SqlConnection(sql.ConnectionString);

sqlConn.Open();

 

SqlCommand sqlCmd = new SqlCommand(sql.Query, sqlConn);

SqlDataReader sdr = sqlCmd.ExecuteReader();

 

int fieldCount = sdr.FieldCount;

 

while (sdr.Read())

{

    try

    {

        object[] args = new object[fieldCount];

        string methodName = "";

        for (int i = 0; i < fieldCount; i++)  //Create test name

        {

            object v = sdr.GetValue(i);

            if (string.IsNullOrEmpty(methodName))

                methodName = v.ToString().Replace(" ", "");

            else

                methodName = methodName + "," + v.ToString().Replace(" ", "");

 

            args[i] = v; //Populate collection

        }

 

        suite.Add(new SqlServerTestMethod(method, method.Name + "(" + methodName + ")", args));

    }

    catch (Exception ex)

    {

        NUnitTestMethod ts = new NUnitTestMethod(method);

        ts.RunState = RunState.NotRunnable;

        ts.IgnoreReason = string.Format(

                "Exception thrown.  Check query and connection strings are correctly for {0}.  Exception {1}", method.Name, ex.Message);

        suite.Add(ts);

    }

}

Figure 15: - Snippet from CreateRowsFromSQL


If an error occurs during the setup we need to alert the user someone.  One approach is use the catch block create another test, set the running state to be Not Runnable and then set a reason, which is displayed to the user in the GUI.  When the tests are executed, they will have a yellow icon and the message will appear under the Tests Not Run tab.

If it was more of a serious error that affects the entire test suite we could set the same properties directly on the test suite.

suite.RunState = RunState.NotRunnable;

suite.IgnoreReason =

    string.Format(

        "SQL Exception thrown.  Check query and connection strings are correctly for {0}.  Exception {1}", suite.TestName, ex.Message);

Figure 16: - Snippet from CreateRowsFromSQL error handling


This code will give us a test suite containing a number of tests based on rows of the query, if our SQL statement returned 10 rows of data then 10 tests would be created in the test suite. The next stage is to define how they can be executed. One approach would be to use the standard NUnitTestMethod, as we did in suite builder.  This does not support parameters and so, as that is a major requirement, we need to create our own TestMethod object.

The first step is to inherit from NUnitTestMethod, which will give us the fundamental support for executing the test. We then need to define our own constructor so we can pass in the arguments to the object.

public SqlServerTestMethod(MethodInfo method, string testName, object[] arguments) : base(method)

{

    _arguments = arguments;

    TestName.Name = testName;

}

Figure 17: - Sql Server Test Method Constructor


Within the constructor, I set the TestName property to be the test name we created based on the row.

The next step is to pass in the arguments to the test.  To do this, we need to override the RunTestMethod that is called to execute the test.  The first task is to ensure we have arguments, if not, then we need to pass in null. We thereby gain access to the current TestSuite in order to access the test fixture.  Finally, we use Reflection to invoke the test method within the test fixture, passing in the arguments as a parameter.

public override void RunTestMethod(TestCaseResult testResult)

{

    object[] arguments = _arguments ?? new object[] {null};

    TestSuite testSuite = (TestSuite) this.Parent.Parent;

    Reflect.InvokeMethod(this.Method, testSuite.Fixture, arguments); //Execute Test

}

Figure 18: - Override Run Test Method


Under the covers, the CLR will set the arguments and do any type conversion required, finally invoking the test just like NUnit does with the [Test] attribute.

In summary, by using the Test Case Builder extension, a single test method can be reused in several different ways to meet your own requirement for defining test values.

Test Decorators

A test decorator allows us to make simple modifications to the test without having a great affect on the way that NUnit processes the test.  Test Decorators allow us to add additional attributes to the standard [Test] that to either define the way the test is executed, set properties on the test such as description, or run commands before/after the test has been executed.  For this extension, I will create a decorate which can execute a SQL Statement before or after, or before and after the test is executed – great for creating and deleting test data.

Figure 19 is an example of how the test decorator could be used. The TestSequence enumeration simply indicates when to execute the query.

[Test]

[ExecuteSql(ExecuteWhen = TestSequence.BeforeAndAfter, Connection = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "DELETE FROM [Order Details]")]

public void Test()

{

    Assert.IsTrue(true);

}

Figure 19: - Sample Usage

All test decorators must implement the ITestDecorator and install into the TestDecorators extension point.  The ITestDecorator has a single method called Decorate. This has the parameters of the constructed Test object (built via the Test attribute) and the member info for the test method.  This member info can be used to access all the metadata about the method, such as attributes.

 Figure 20 is the Decorate method for the ExecuteSQL extension.  The first task is to check that the test is actually a NUnitTestMethod (if you want to support other Test Case Builders then you will need to add checking for this) to ensure it is not a test suite.  I then get the attributes for the method, and for each attribute of type ExecuteSqlAttribute I add it to the list.  Once I have finished processing, I then create an ExecuteSqlTestMethod so I can configure how the test is executed , I also pass in the collection of ExecuteSqlAttributes as a parameter.    The purpose of processing all the attributes as a list is to allow support for multiple execute sql attributes per test method, each of which could define a different query and test sequence.

public Test Decorate(Test test, System.Reflection.MemberInfo member)

{

    if (test is NUnitTestMethod)

    {

        List<ExecuteSqlAttribute> methodAttributes = new List<ExecuteSqlAttribute>();

 

        Attribute[] attributes = Reflect.GetAttributes(member, ExecuteSqlAttribute, false);

 

        if (methodAttributes.Count != attributes.Length)  //Already processed

        {

            foreach (Attribute attr in attributes) //Need to convert them all

            {

                ExecuteSqlAttribute sqlAttribute = attr as ExecuteSqlAttribute;

                if (sqlAttribute != null && !methodAttributes.Contains(sqlAttribute))

                {

                    methodAttributes.Add(sqlAttribute);

                }

            }

 

            test = new ExecuteSqlTestMethod(((NUnitTestMethod) test).Method, methodAttributes);

        }

    }

    return test;

}

Figure 20: - Test Decorator for Execute SQL

 

The ExecuteSqlTestMethod inherits from NUnitTestMethod, in order to customise the Run method.  Our run method executes any sql attributes marked as being ‘required to execute’ Before or BeforeAndAfter, it then calls the Run method on the base object (NUnitTestMethod) thereby allowing all of the additional processing, such as error checking, to take place via NUnit.  Finally it does another search for all the sql attributes which are marked After or BeforeAndAfter.  If it finds an attribute it will call the ExecuteSql method (Figure 21).

public override void Run(TestCaseResult testResult)

{

    try

    {

        foreach (ExecuteSqlAttribute attribute in _sqlAttributes)

        {

            if (attribute.ExecuteWhen == TestSequence.Before ||

                attribute.ExecuteWhen == TestSequence.BeforeAndAfter)

                ExecuteSql(attribute);

        }

 

        base.Run(testResult);

 

        foreach (ExecuteSqlAttribute attribute in _sqlAttributes)

        {

            if (attribute.ExecuteWhen == TestSequence.After ||

                attribute.ExecuteWhen == TestSequence.BeforeAndAfter)

                ExecuteSql(attribute);

        }

    }

    catch (System.Data.SqlClient.SqlException ex)

    {

        testResult.Failure("SQL Exception Thrown from TempFileAttribute: " + ex.Message, ex.StackTrace);

 

    }

}

Figure 21: - Execute Sql Run Method

                                   

private void ExecuteSql(ExecuteSqlAttribute attribute)

{

    SqlConnection sqlConn = null;

    try

    {

        sqlConn = new SqlConnection(attribute.Connection);

        sqlConn.Open();

 

        SqlCommand sqlCmd = new SqlCommand(attribute.Script, sqlConn);

        sqlCmd.ExecuteNonQuery();

    }

    finally

    {

        if (sqlConn != null && sqlConn.State != ConnectionState.Closed)

            sqlConn.Close();

    }

}

Figure 22: - Execute Sql ExecuteSql method

 

This decorator can then be used to execute sql commands during the execution of the test itself.  This is great for defining SQL to be executed on a test-by-test basis.  If the query execution fails then the test will fail, but with some correct try...catch blocks you can change this.

[Test]

[ExecuteSql(ExecuteWhen = TestSequence.BeforeAndAfter, Connection = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "DELETE FROM [Order Details]")]

[ExecuteSql(ExecuteWhen = TestSequence.Before, Connection = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "select 'before'")]

[ExecuteSql(ExecuteWhen = TestSequence.After, Connection = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "select 'after'")]

public void Test()

{

    Assert.IsTrue(true);

}

 

Event Listeners

As mentioned earlier, event listeners are currently broken in 2.4.6; this should be fixed in later releases.

Event Listeners allow you to hook into various events that NUnit fires at various points in the test suite execution.   This is great for providing additional reporting functionality, which is the example I will do so every event will write information out to the Debug listener.

In order to create an event listener addin, the class needs to inherit from the EventListener object. Figure 23 is a list of all the methods on the object where you can provide implementation hooks.  Note that all the methods must be included in the addin; the actual method body can be empty if you do not wish to do anything with that event.

#region EventListener Members

public void RunFinished(Exception exception);

public void RunFinished(TestResult result);

public void RunStarted(string name, int testCount);

public void SuiteFinished(TestSuiteResult result);

public void SuiteStarted(TestName testName);

public void TestFinished(TestCaseResult result);

public void TestOutput(TestOutput testOutput);

public void TestStarted(TestName testName);

public void UnhandledException(Exception exception);

#endregion

Figure 23: - Event Listener events


An example of how to implement the code for the different events is shown in figure 24.

public void RunStarted(string name, int testCount)

{

    Debug.Write("Test Started " + name + " " + testCount);           

}

 

public void RunFinished(TestResult result)

{

    Debug.Write("Test Finished " + name + " " + result.ResultState);

}

Figure 24: - Event Listener implementation


Finally, to install the addin we need to use the extension point named “EventListeners”.

public bool Install(IExtensionHost host)

{

    IExtensionPoint listeners = host.GetExtensionPoint("EventListeners");

    if (listeners == null)

        return false;

 

    listeners.Install(this);

    return true;

}

Figure 25: - Event Listener Installation Code


Once the addin has been deployed, it will automatically take affect for any NUnit tests executed.  There is no need to modify the tests.

Deployment and execution

After you have successfully created your required addin, you will need to actually deploy and install it.  One limitation of the addins is that they can only be installed into the same version of NUnit as they were compiled against; so, if someone compiled an addin against 2.4.5, then it wouldn’t work in 2.4.6. 

To install the addin, you need to copy the assembly into the addin folder in your NUnit folder.  For example, C:\Program Files\NUnit\2.4\addins\, this could vary on different NUnit installations.  You will also need to install the addin on any machines that will execute the test suite, such as build servers.  When NUnit loads, it automatically searches for assemblies in this directory and attempts to load them.

In terms of executing tests containing your addin, the NUnit GUI and console application will support your addin correctly.  However, support within 3rd party runners depends on the runner.  TestDriven.Net works with all types of NUnit extensions, whereas ReSharper does not support any type of extensions.  This is something to keep in mind when developing and executing.

Once your addin is installed and your tests are executing, you might find that you need to debug into your assembly to fix problems. The solution is to use the Attach to Process option within Visual Studio.  After you have set breakpoints within your code, I always

  1. start the NUnit exe,
  2. attach to the NUnit process,
  3. load my test assembly using the addin

at which point your debugger should kick it and allow you to step through your code.

Summary

In summary, NUnit 2.4.6 has a number of different ways of extending the framework for your own requirements and to tailor test behaviour for different scenarios.  The article has demonstrated how to extend NUnit via the built-in extension points which are Suite Builders, Test Case Builders, Test Decorators and Event Listeners.


© Simple-Talk.com