Click here to Skip to main content
15,884,770 members
Articles / Programming Languages / C#
Tip/Trick

Unit Testing Interfaces in .NET

Rate me:
Please Sign up or sign in to vote.
4.82/5 (16 votes)
13 Aug 2014CPOL2 min read 47.5K   462   28  
A nice pattern to unit test 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 examples are written using MSTest in Visual Studio 2012.

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.

C#
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.

C#
/// <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);
    }
}
C#
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.

C#
[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.

C#
[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.

 

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.

C#
[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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
I like to code and work mainly in .NET although i'm moving into other areas nowadays.
When i'm not coding I like to ride my bike

TFS Productivity Pack

Blog

Comments and Discussions

 
-- There are no messages in this forum --