Unit Testing Interfaces in .NET

Introduction

I want to demonstrate a nice time saving pattern for testing interfaces. It allows us to write the unit tests for an interface just once and use these tests to test any number of implementations of our interface.

The article and example source code originally published on codeproject.

Using the Code

String Searcher Interface

First, we need to define an interface for the example. Here is a simple interface to search strings. The example code contains a few different implementations of the string search algorithm. The implementations of these algorithms are from another CodeProject article by Carl Daniel.

public interface IStringSearcher
{
    /// <summary>
    /// Search for the given pattern
    /// </summary>
    /// <param name="stringToSearch"></param>
    /// <param name="pattern"></param>
    /// <returns></returns>
    IEnumerable<int> SearchString(string stringToSearch, string pattern);

    /// <summary>
    /// Search for the given pattern from the starting index (zero-based)
    /// </summary>
    /// <param name="stringToSearch"></param>
    /// <param name="pattern"></param>
    /// <param name="startingIndex"></param>
    /// <returns></returns>
    IEnumerable<int> SearchString(string stringToSearch, string pattern, int startingIndex);
}

Then, we have an abstract implementation of the interface StringSearcherBase which contains a few useful protected methods that our string search algorithms are going to need.

Finally, a few implementations of the algorithm. One that uses String.IndexOf, and another that uses the Bayer-Moore Algorithm + a few others.

/// <summary>
/// Use the .NET method String.IndexOf to do the searching
/// </summary>
public class StringSearcherIndexOf : StringSearcherBase
{
    public override IEnumerable<int> SearchString
    	(string stringToSearch, string pattern, int startingIndex)
    {
        int patternLength = pattern.Length;
        int index = startingIndex;
        do
        {
            index = stringToSearch.IndexOf(pattern, index,
                StringComparison.InvariantCultureIgnoreCase);
            if (index < 0)
                yield break;
            yield return index;
            index += patternLength;
        }
        while (true);
    }
}

 

public class StringSearcherBoyerMoore : StringSearcherBase
{
    public override IEnumerable<int> SearchString(string stringToSearch, string pattern,
        int startingIndex)
    {
        int[] badCharacterShift = BuildBadCharacterShift(pattern);
        int[] suffixes = FindSuffixes(pattern);
        int[] goodSuffixShift = BuildGoodSuffixShift(pattern, suffixes);

        int patternLength = pattern.Length;
        int textLength = stringToSearch.Length;

        int index = startingIndex;
        while (index <= textLength - patternLength)
        {
            int unmatched;
            for (unmatched = patternLength - 1;
                unmatched >= 0 &&
                	(pattern[unmatched] == stringToSearch[unmatched + index]);
                --unmatched)
                ; // empty

            if (unmatched < 0)
            {
                yield return index;
                index += goodSuffixShift[0];
            }
            else
                index += Math.Max(goodSuffixShift[unmatched],
                    badCharacterShift[stringToSearch[unmatched + index]] -
                    	patternLength + 1 + unmatched);
        }
    }
}

Writing the Unit Tests

Having to write a set of tests for each implementation of the IStringSearcher interface would be a massive waste of time. What we want to do is write the tests once and reuse them to test all the implementations of the string search algorithm.

To do this, we are going to write an abstract base class where the tests will actually be written. Note the abstract method GetStringSearcherInstance.

[TestClass]
public abstract class StringSearcherTestBase
{
    /// <summary>
    /// Override this method to implement the tests
    /// </summary>
    /// <returns></returns>
    public abstract IStringSearcher GetStringSearcherInstance();

    [TestMethod]
    public void BasicTest()
    {
        IStringSearcher searcher = GetStringSearcherInstance();
        List<int> indexes = searcher.SearchString(
            "Hello. Welcome to unit testing interfaces",
            "test").ToList();

        Assert.AreEqual(1, indexes.Count);
        Assert.AreEqual(23, indexes[0]);
    }

    [TestMethod]
    public void NegativeTest()
    {
        IStringSearcher searcher = GetStringSearcherInstance();
        var indexes = searcher.SearchString(
            "Hello. Welcome to unit testing interfaces",
            "uint").ToList();

        Assert.AreEqual(0, indexes.Count);
    }
}

Then, we simply implement this test class for each of the string algorithms we want to test and override the GetStringSearcherInstance method to return the IStringSearcher class we want to test

[TestClass]
public class StringSearcherBoyerMoore_Tests : StringSearcherTestBase
{
    public override IStringSearcher GetStringSearcherInstance()
    {
        return new StringSearcherBoyerMoore();
    }
}

Looking in the test explorer, we see each test available for the implementation we are testing. It’s as simple as that.

unittestinterface1

Moreover, if for some subtle reason, I want to make a test different in one of the implementations, then I can mark it as virtual in the test base class and override it.

For example, if the startingIndex is out of bounds, then all the string search classes just return no matches, but StringSearcherIndexOf throws an exception. So this test is overridden in StringSearcherIndexOf_Tests to test for the exception.

[TestClass]
public class StringSearcherIndexOf_Tests : StringSearcherTestBase
{
    public override IStringSearcher GetStringSearcherInstance()
    {
        return new StringSearcherIndexOf();
    }

    /// <summary>
    /// Overridden to test an ArgumentOutOfRangeException is thrown
    /// </summary>
    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public override void NegativeTest_StartingIndexOutOfBounds()
    {
        IStringSearcher searcher = GetStringSearcherInstance();
        var indexes = searcher.SearchString(
            "Hello. Welcome to unit testing interfaces",
            "unit",
            500).ToList();
    }
}

Points of Interest

The above was done using MSTest. I haven’t tried with any other testing frameworks. It would be interesting to see how other unit test frameworks handle it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s