Click here to Skip to main content
15,891,423 members
Articles / Programming Languages / C#
Article

Using Interfaces For Testing

Rate me:
Please Sign up or sign in to vote.
4.11/5 (2 votes)
5 Nov 20064 min read 19.2K   23  
Examples of hard to test code and the usage of the pattern.

Introduction

I'm now going to talk about a pattern that can used to test code that's usually hard to test. It requires generating an interface purely for the purpose of testing that's used only within a unit test. In order to demonstrate this, let me use a code sample. Below is the class TheClass I want to test. We want to test the behavior of the method OurAwesomeMethod(). However, this calls TrickyMethod(), which for some reason we don't want to call. At the end of this article, I'll provide concrete examples of why this might be necessary.

C#
public class TheClass
{
    public string OurAwesomeMethod(string input)
    {
        //code to test
        string output = TrickyMethod(input);
        // code to test
        return output;
    }

    protected virtual string TrickyMethod(string input)
    {
        throw new NotImplementedException();
    }
}

The traditional solution in such cases is to use a stub of the class we're testing. In order to make sure the method TrickyMethod() is actually called, we often use a call-count as demonstrated below.

C#
private class TheClassStub : TheClass
{
    public int CallCount = 0;

    protected override string TrickyMethod(string input)
    {
        CallCount++;
        return input;
    }
}

As demonstrated below, we can now write as many tests as we want. In each test, we can now assert that the method is actually called as many times as we expect using the call count.

C#
[Test]
public void OurAwesomeMethodDoesTheRightThing()
{
    string output = _theClassStub.OurAwesomeMethod("hello");
    Assert.AreEqual("hi",output);
    Assert.AreEqual(1,_theClassStub.CallCount);
}

However, this does not allow us to verify that the method is actually called with the correct input. It also does not allow us to verify that OurAwesomeMethod() processes the output of TrickyMethod() correctly for all outputs. We could get around this by putting code into the stub. But the more the number of test cases, the more code we'd have to put in.

Instead, here is an alternate approach. First, we build an interface with the same method TrickyMethod(). The interface is used only in the test class.

C#
public interface ITheClassForTest
{
     string TrickyMethod(string input);
}

The next step is to rebuild the stub with some modifications. It accepts this interface in the constructor, and calls TrickyMethod() when the same method is invoked in the stub.

C#
private class TheClassStub : TheClass
{
    private ITheClassForTest _theClassForTest;

    public TheClassStub(ITheClassForTest theClassForTest)
    {
        _theClassForTest = theClassForTest;
    }

    protected override string TrickyMethod(string input)
    {
        return _theClassForTest.TrickyMethod(input);
    }
}

Now that we have this in place. We can write our tests. Perhaps, the SetUp method in our test will look like this:

C#
private TheClassStub _theClassStub;
private ITheClassForTest _theClassForTest;

   [SetUp]
   public void SetUp()
   {
       _theClassForTest = _mockery.NewMock<ITHECLASSFORTEST>();
       _theClassStub = new TheClassStub(_theClassForTest);
   }

What we can now do is use the mock _theClassForTest to set expectations in each test and make sure that TrickyMethod() is called as many times as we expect, with the parameters we expect, and returns whatever we wish to, allowing us to write stricter tests as demonstrated below.

C#
[Test]
public void OurAwesomeMethodDoesTheRightThing()
{
  Expect.Once.On(_theClassForTest).Method(
         "TrickyMethod").With("hello").Will(Return.Value("hi"));
  string output = _theClassStub.OurAwesomeMethod("hello");
  Assert.AreEqual("hi",output);
}

This way, we can write a whole suite of tests, each asserting different behavior of OurAwesomeMethod() with respect to TrickyMethod().

Lastly, I'm going to list three examples of classes where we might want to use this method to test.

C#
public partial class _Default : Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    
    public bool PerformUserLoginTask(string userName, string password)
    {
        bool isUserValid = false;
        //call to check users identity
        if (isUserValid)
        {
            authenticateUser(userName);
            return true;
        }
        else return false;                   
    }

    protected virtual void authenticateUser(string userName)
    {
        Response.SetCookie(FormsAuthentication.GetAuthCookie(userName, true));
    }
}

In this case, the challenge is the call to authenticateUser(). As you you can see, there are calls to FormsAuthentication.GetAuthCookie() and Response.SetCookie. The assumption here is that this is the only place in the code where we have calls to these, and we are confident there will be no further calls.

Neither of these can be mocked directly. We could create singleton classes that have the same methods and delegate calls forward but that seems like overkill. We could push that authenticateUser() into a seperate class and inject a mock, but again that seems like overkill, and it's modifying our design considerably just for the purpose of easy testability.

Let's look at another example.

C#
public abstract class ComplexAbstractClass
{
    public virtual string DoSomethingComplex(string input)
    {
        // complex recursive logic here
        string substring = input.Substring(3, 10);
        substring = DoSomethingComplex(substring);
        if (substring.Length > 17)
        {
            substring = ModifyThis(substring);
        }
        return substring;
    }

    protected abstract string ModifyThis(string substring);
}
C#
public class ComplexClassImpl1 : ComplexAbstractClass
{
    private string _var;

    public ComplexClassImpl1(string var)
    {
        _var = var;
    }

    protected override string ModifyThis(string substring)
    {
        string workWith =
           ConfigurationService.instance.getSetting(substring,_var);
        //do stuff with WorkWith
        return workWith;
    }
}

In this case, the challenge is we have an abstract class with some complex recursive logic. We could write three or four tests around it to see every possible outcome. And then there are a bunch of concrete implementations which again have their own complex logic. We could test everything using one such implementation, but we'd have to have a large number of complex tests that test multiple pieces of logic together.

Additionally, we would have to repeat some of the tests for the other implementations, to make sure this logic is working correctly in each case. Ideally, we would like to decouple the two cases and test them seperately.

Here is example three. An ugly piece of code unfortunately, but let's assume that because of circumstances and since this is the only place where such logic is encapsulated, this is the best we can do.

C#
public class UglyClass
{
    public bool validateUser(string userName,string password)
    {
       //do stuff
        return checkCondition(3);
    }

    protected virtual bool checkCondition(int i)
    {
        if (i == 0) return false;
        return checkSomethingElse(i);
    }

    protected virtual bool checkSomethingElse(int i)
    {
        string setting =
          ConfigurationService.Instance.getSetting(i.ToString(),"a");
        if (setting == "5")
        return true;
        else return false;
    }
}

As in the previous examples, the tricky problem here is, testing validateUser in one go will require us to test behavior with large sets of input. Though we have a large number of small methods that do specific things, our test is trying to assert their behavior all together. Again in such a case, there might be times where we would benefit from trying to test each method seperately. It does go against the grain of unit testing in some sense, but on the other hand, it might be the most practical solution in a case like this.

In all such cases, the pattern described above might be useful. Please feel free to email me or comment on this topic. I have added at the bottom three test classes, one for each of the cases mentioned above, demonstrating this method of testing.

C#
[TestFixture]
public class TestPage
{
    Mockery _mock = new Mockery();
    [Test]
    public void PerformUserLoginTaskDoesWhatItShould()
    {
        IDefaultForTest mockDefault = _mock.NewMock<IDEFAULTFORTEST>();
        _DefaultStub stub = new _DefaultStub(mockDefault);
        Expect.Once.On(mockDefault).Method(
                       "authenticateUser").With("user");
        bool result = stub.PerformUserLoginTask("user", "pass");
        Assert.IsTrue(result);
    }

    private class _DefaultStub : _Default
    {
        IDefaultForTest _mockForTest;
        public _DefaultStub(IDefaultForTest mockForTest)
        {
            _mockForTest = mockForTest;
        }
        protected virtual void authenticateUser(string userName)
        {
            _mockForTest.authenticateUser(userName);
        }
    }
    public interface IDefaultForTest
    {
        void authenticateUser(string userName);
    }
}
C#
[TestFixture]
public class UglyClassTest
{
    private Mockery _mockery = new Mockery();
    private IUglyClassForTest _uglyClassForTest;
    private UglyClassStub _uglyClassStub;

    [SetUp]
    public void SetUp()
    {
        _uglyClassForTest = _mockery.NewMock<IUGLYCLASSFORTEST>();
        _uglyClassStub = new UglyClassStub(_uglyClassForTest);
    }

    [Test]
    public void ValidateUserCallsCheckConditionWith3()
    {
        Expect.Once.On(_uglyClassForTest).Method(
            "checkCondition").With(3).Will(Return.Value(true));
        bool result = _uglyClassStub.validateUser("user", "pass");
        Assert.IsTrue(result);
    }

    [Test]
    public void checkConditionCallsCheckSomethingElse()
    {
        Expect.Once.On(_uglyClassForTest).Method(
            "checkSomethingElse").With(27).Will(Return.Value(false));
        bool result = _uglyClassStub.callCheckCondition(27);
        Assert.IsFalse(result);
    }

    [Test]
    public void checkSomethingElsedoesblah()
    {
        //test stuff
    }

    public interface IUglyClassForTest
    {
        bool checkCondition(int i);
        bool checkSomethingElse(int i);
    }
    private class UglyClassStub : UglyClass
    {
        IUglyClassForTest _uglyClassForTest;

        public UglyClassStub(IUglyClassForTest uglyClassForTest)
        {
            _uglyClassForTest = uglyClassForTest;
        }

        public bool callCheckCondition(int i)
        {
            return base.checkCondition(i);
        }

        public bool callCheckSomethingElse(int i)
        {
            return base.checkSomethingElse(i);
        }

        protected override bool checkCondition(int i)
        {
            return _uglyClassForTest.checkCondition(i);
        }

        protected override bool checkSomethingElse(int i)
        {
            return _uglyClassForTest.checkSomethingElse(i);
        }
    }
}
C#
[TestFixture]
public class ComplexAbstractClassTest
{
    Mockery _mock = new Mockery();
    public void ComplexAbstractClassCallsModifyThisWithRequiredString()
    {
        IComplexAbstractClassForTest _complexabstractClassForTest =
          _mock.NewMock<ICOMPLEXABSTRACTCLASSFORTEST>();
        Expect.Once.On(_complexabstractClassForTest).Method(
         "ModifyThis").With("hik").Will(Return.Value("hum"));
        ComplexAbstractClassStub stub =
            new ComplexAbstractClassStub(_complexabstractClassForTest);
        string output = stub.DoSomethingComplex("ihenelfhikkkjo");
        Assert.AreEqual("hum",output);
    }
    public interface IComplexAbstractClassForTest
    {
        string ModifyThis(string substring);
    }
    private class ComplexAbstractClassStub : ComplexAbstractClass
    {
        IComplexAbstractClassForTest _complexAbstractClassForTest;
        public ComplexAbstractClassStub
            (IComplexAbstractClassForTest complexAbstractClassForTest)
        {
            _complexAbstractClassForTest = complexAbstractClassForTest;
        }

        protected override string ModifyThis(string substring)
        {
            return ModifyThis(
              _complexAbstractClassForTest.ModifyThis(substring));
        }
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --