Implementing Tests Using Attributes

Introduction - Programming Language - Prerequisites - Creating The Test Fixture - Adding A Test - SetUp and TearDown - Ignoring A Test Or A Test Fixture - Summary

Introduction

Starting with version 1.7.3 csUnit supports two different ways for decorating tests:

  • Decoration based on naming conventions
  • Decoration based on attributes

This tutorial demonstrates how to use attributes for decorating and implementing your tests.

Programming Language

The tutorial uses C#.

Prerequisites

This tutorial makes the following assumptions:

  • Visual Studio .NET 2002/2003 and csUnit 1.7.3 or later are installed
  • You have set up a project where you want to place your test fixtures. This project can be empty.

Creating The Test Fixture

The first step is to create the test fixture. That is the class which will contain the tests you plan to write. In our projects we typically use the convention <classToBeTested>Tests, where <classToBeTested> is replaced by the name of the class we want to test. For instance, if the class you want to implement is going to be called Foo then we would call the test fixture FooTests. This is also the name I will also use for this tutorial. Of course, for your projects, you can choose any name you like!

You create the test fixture by adding a class to your project. Use FooTests as the class name. The class has no base class and it does not implement any interface. The resulting file should have the name FooTests.cs and it should contain the following code:

using System;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    public class FooTests() {
       public FooTests() {
          //
          // TODO: Add constructor logic here
          //
       }
    }
}

With this code alone, csUnit will not be able to identify your test. In order to achieve this the attribute TestFixture has to be tagged to the class:

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    public class FooTests() {
       public FooTests() {
          //
          // TODO: Add constructor logic here
          //
       }
    }
}

The TestFixture attribute takes no parameters and can be applied to classes only. It serves only identification purposes. Upon loading the assembly csUnit searches the entire assembly for classes with this custom attribute set. Don't forget to add a reference to the csUnit assembly to your projects references, and also add the using directive to the source code (see sample above).

Adding a Test

The next step is to add a test method. Let's assume we want to call it MyFirstTest(). Then you would add the following lines to implement the test:

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    public class FooTests() {
       public FooTests() {
          //
          // TODO: Add constructor logic here
          //
       }

       public void MyFirstTest() {
          // your test implementation goes here
       }
    }
}

Again, csUnit does not know about the test yet. In order to tag it as a test, the attribute Test is used. This attribute can be applied to methods only. So this is what you have to do (Note: I deleted the constructor as it is not needed):

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    public class FooTests() {
       [Test]
       public void MyFirstTest() {
          // your test implementation goes here
       }
    }
}

When the assembly is loaded csUnit iterates over all public classes it can find. For each of the classes csUnit iterates over all public methods. If the method has a signature of "public void foo()" and it carries the custom attribute test, it is identified as a test. You can choose any name for the test you like, if you use the attribute test.

SetUp and TearDown

In some cases you want to able to run all tests within a test fixture using the same start conditions. On the other hand you do not want to duplicate the setup code. Furthermore, you want to be sure, that the setup and the teardown code is executed for each single test in the fixture.

Furthermore you want to be able to also cleanup the objects you might have created during setup. And you want to be sure that this is done automatically.

csUnit provides two attributes for achieving exactly this: SetUp and TearDown. The following code sample shows how such attributes can be used.

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    public class FooTests() {
       [SetUp]
       public void SetUp() {
          _myTestData = new TestData();
       }

       [TearDown]
       public void CleanUp() {
          _myTestData.Dispose(); // assumes IDisposable is implemented
          _myTestData = null;
       }

       [Test]
       public void MyFirstTest() {
          // your test implementation goes here
       }

       private TestData _myTestData = null;
    }
}

Again, the signature of both, the SetUp and the TearDown method, must be "public void foo()". And again, csUnit searches each test fixture for the existence of these attributes. If it finds one of them or both, the SetUp-method will be called immediately before each test, and the TearDown-method will be called immediately after each test.

Ignoring A Test Or A TestFixture

In some cases you might want to not execute a test, e.g. because its implementation is not complete yet. You can achieve this by adding the Ignore attribute to the test. The ignore attribute can be applied to both a single test or to an entire test fixture. It takes one parameter which is a string, where you can indicate the reason why the test should be skipped:

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    public class FooTests() {
       [Test]
       public void MyFirstTest() {
          // your test implementation goes here
       }

       [Test, Ignore("Implementation not complete yet.")]
       public void SkipThisOne() {
       }
    }
}

When csUnit finds the Ignore attribute on a test method, it retrieves the reason from the attribute (that is the string that has been added there) and reports it in the UI as can be seen in the following example:

csUnit screen

In some even more rare cases, you want to exclude an entire test fixture from execution. In this case simply apply the Ignore attribute to the class:

using System;
using csUnit;

namespace example {
    /// <summary>
    /// Summary description for FooTests.
    /// </summary>
    [TestFixture]
    [Ignore("Fixture is of no use yet")]
    public class FooTests() {
       [Test]
       public void MyFirstTest() {
          // your test implementation goes here
       }
    }
}

If the Ignore attribute is put on a test fixture, it will not even be instantiated during running the tests. csUnit accesses only the type information including the custom attributes for the test fixture and the tests contained in it.

Summary

As you have seen, using attributes is a convenient means for implementing tests. If you prefer the style which is based on naming conventions: csUnit still supports it. Furthermore: You can mix the attribute base and the naming convention based approach as you like.

Happy Testing!

Home

Download

Documentation

Features

Tutorials

Support

Suggest A Feature

Report A Bug

About



Web csunit.org

Sponsors:

Extreme Simplicity Logo

Agile Utilities Logo

Blue Note Ventures Logo


Sources hosted by
Get csUnit - unit testing for .NET at SourceForge.net. Fast, secure and Free Open Source software downloads



Copyright © 2002-2008 by Agile Utilities NZ Ltd. All rights reserved. Site design by Andreas Weiss. This site is protected by bot traps.