Click here to Skip to main content
6,596,602 members and growing! (23,126 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » .NET Framework » General     Intermediate License: The Code Project Open License (CPOL)

Example Code And Quick Refresher on Unit Tests with Stubs and Mocks

By Vishnu S. Iyengar

Example Code And Quick Refresher on Unit Tests with Stubs and Mocks
C# 2.0, Windows, .NET 2.0VS2005, Dev
Posted:29 Oct 2006
Views:9,607
Bookmarked:10 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
1 vote for this article.
Popularity: 0.00 Rating: 4.00 out of 5

1

2

3
1 vote, 100.0%
4

5

Introduction

The main purpose of this article is to display the code that is used as reference in the other article. It is also a quick refresher of what I'm looking for in Unit tests that justify the new pattern. So I have written a small class and a test around it that uses stubs and mocks.

So for our example codebase, we have:

  1. A class that is some sort of configuration service which can be called from various places in the code to look up specific entries or settings. The implementation chosen is a singleton.
  2. Some sort of Database Service. Note that while it's not a singleton, we have a factory that can generate instances.
  3. A core class which seems to be doing something important. The StuffDoer.
  4. And lastly our class which we are going to write tests for. The method we're going to test is OurAwesomeMethod().
  public interface IConfigurationService
    {
        string getSetting(string id1, string id2);
    }
    public class ConfigurationService : IConfigurationService
    {
        private ConfigurationService()
        {
        }

        private static IConfigurationService _instance;

        public static IConfigurationService instance
        {
            get
            {
                if (_instance == null)
                    _instance = new ConfigurationService();
                return _instance;
            }
            set { _instance = value; }
        }
        public string getSetting(string id1, string id2)
        {
            string blah = null;
            // complex code here;
            return blah;
        }
    }
  
public interface IDataService
    {
        bool IsUserValid(string name, string password);
    }

    public class DataService : IDataService
    {
        string _connectionString;
        public bool IsUserValid(string name, string password)
        {
            bool value = false;
            string.Concat(_connectionString, name);
            //complex checks here;
            return value;
        }

        public DataService(string connectionString)
        {
            _connectionString = connectionString;
        }
    }

    public static class ServiceFactory
    {
        private static IDataService _instance = null;

        public static IDataService GetGetDataService(string connectionString)
        {
            if (_instance == null)
                return new DataService(connectionString);
            else return _instance;
        }

        public static void InjectForTest(IDataService testDataService)
        {
            _instance = testDataService;
        }
    }
 
   public interface IstuffDoer
    {
        string DoStuff(string input);
    }
   public class StuffDoer : IstuffDoer
    {
        private string _connectionString;


        public StuffDoer(string connectionString)
        {
            _connectionString = connectionString;
        }

        public string DoStuff(string input)
        {
            string output = "1" + input + "0";
            //complex code here
            IDataService dataService = 
                ServiceFactory.GetGetDataService(_connectionString);
            // calls to dataservice here
            return output;
        }
    }

        public class OurClass
    {
        private string _connectionStringSettingName;
        private int _minUsers;

        public OurClass(string connectionStringSettingName, int minUsers)
        {
            _connectionStringSettingName = connectionStringSettingName;
            _minUsers = minUsers;
        }

        public string OurAwesomeMethod(Dictionary<STRING, string> users)
        {
            string connectionString = ConfigurationService.instance.getSetting
                    ("db", _connectionStringSettingName);
            List<STRING> validUsers = getValidUsers(connectionString, users);
            if (validUsers.Count < _minUsers)
                throw new ApplicationException("not enough users");
            IstuffDoer stuffDoer = new StuffDoer(connectionString);
            string output = string.Empty;
            foreach (string user in validUsers)
            {
                 output = output + stuffDoer.DoStuff(user);
            }
            return output;
        }

        private static List<STRING> getValidUsers(string connectionString,
                                                  Dictionary<STRING, string> users)
        {
            IDataService dataService = 
                ServiceFactory.GetGetDataService(connectionString);
            List<STRING> validUsers = new List<STRING>();
            foreach (KeyValuePair<STRING, string> user in users)
            {
                string userName = user.Key;
                string password = user.Value;
                if (dataService.IsUserValid(userName, password))
                {
                    validUsers.Add(userName);
                }
            }
            return validUsers;
        }
    }

Our purpose here is to test OurClass.OurAwesomeMethod(). Generally, it is accepted that tests should be small and test very specific behaviours/execution branches of a method. The complete set of tests around a method are used to validate the behavior of this method. So specifically unit tests have two purposes:

  • Small discrete tests checking specific behaviors of a method
  • Small discrete tests documenting specific behaviors of a method

So as a result, normally tests must be:

  • Correct - when the method doesn't do what is intended, the test must break.
  • Robust - when the method continues to behave right despite changes in implementation, the test should not break as far as possible.
  • Clear - When someone reads a test, it should be possible to ascertain one additional thing that the method that is being tested does.

Ok, given all of this, the next step is to write a test. The assumption being made here is that we want to separate all the decoupled logic and test the behavior we have. There are three calls being made outside our own class. The dataservice and the configurationservice can easily be dealt with by sending in mocks. However, as we can see, the call to StuffDoer() is a little harder. This calls for some minor refactoring of our method and a stub. The refactoring is easily done. So our new and improved OurAwesomeMethod() is as follows:

public string OurAwesomeMethod(Dictionary<STRING, string> users)
{
    string connectionString = ConfigurationService.instance
        .getSetting("db", _connectionStringSettingName);
    List<STRING> validUsers = getValidUsers(connectionString, users);
    if (validUsers.Count < _minUsers)
        throw new ApplicationException("not enough users");
    string output = string.Empty;
    IstuffDoer stuffDoer = getStuffDoer(connectionString);
    foreach (string user in validUsers)
    {
        output = output + stuffDoer.DoStuff(user);
    }
    return output;
}

protected virtual IstuffDoer getStuffDoer(string connectionString)
{
    return new StuffDoer(connectionString);
}

Now a stub can easily prevent a real call from going through. So we can build such a stub inside our testclass:

private class OurClassStub : OurClassRefactored
{
    private IstuffDoer _stuffDoer;

    public OurClassStub(string connectionStringSettingName, 
    int minUsers, IstuffDoer stuffDoer)
    : base(connectionStringSettingName, minUsers)
    {
        _stuffDoer = stuffDoer;
    }

    protected override IstuffDoer getStuffDoer(string connectionString)
    {
        return _stuffDoer;
    }
}

Now when we call OurAwesomeMethod(), we can make sure that all external calls are sent to mock objects. For the purpose of this document, I've decided to use RhinoMocks since they are pretty cool. They have good support for refactoring which is definitely something I'm interested in. So let's see what we can say about OurAwesomeMethod().

  1. We know that everytime we call it, it really needs the connectionString. So it's going to ask the Configuration Service for it. We don't care how many times it asks for it or when. All we care is that if it asks for a connectionString we give it one. We're gonna pass to OurAwesomeMethod() a dictionary with username/passwords and we need to tell our dataservice to return information about validity of each such combination. And now if we create OurClass() with a lower tolerance for valid users than there are, it must throw an exception.
  2. We also know that when we do the same thing as before but the number of users who it checks the validity of is greater than or equal to its tolerance, it must call StuffDoer to do stuff on each of these and then return a concatenated list of all these users.

So let's write up the test class to test both these scenarios:

[TestFixture]
public class OurClassTest
{
    private MockRepository _mocks;
    private IConfigurationService _configurationService;
    private IDataService _dataService;
    private IstuffDoer _stuffDoer;
    private OurClassStub _ourClassStub;
    private const string _connectionStringSettingName = "settingName";
    private const int _tolerance = 3;
    private const string _connectionString = "connectionString";
    private Dictionary<string, string> _users;

    [SetUp]
    public void SetUp()
    {
        _mocks = new MockRepository();
        _configurationService = (IConfigurationService)
                                    _mocks.CreateMock(typeof (IConfigurationService));
        ConfigurationService.instance = _configurationService;
        _dataService = (IDataService)
        _mocks.CreateMock(typeof (IDataService));
        _stuffDoer = (IstuffDoer) _mocks.CreateMock(typeof (IstuffDoer));
        ServiceFactory.InjectForTest(_dataService);
        _users = getUserList();
        _ourClassStub = new OurClassStub(_connectionStringSettingName,
        _tolerance, _stuffDoer);
        SetupResult.On(_configurationService).Call(_configurationService.
        getSetting("db", _connectionStringSettingName)).Return(
        _connectionString);
    }

    private Dictionary<string, string> getUserList()
    {
        Dictionary<string, string> users = new Dictionary<string, string>();
        users.Add("user1", "pass");
        users.Add("user2", "pass");
        users.Add("user3", "pass");
        users.Add("user4", "pass");
        return users;
    }

    private void setupDataServiceResult(string name, string pass, bool result)
    {
        SetupResult.On(_dataService).Call(_dataService.IsUserValid(name, pass))
        .Return(result);
    }

    [Test, ExpectedException(typeof(ApplicationException),"not enough users")]
    public void OurAwesomeMethodThrowsExceptionIfMinimumValidUsersDontExist()
    {
        setupDataServiceResult("user1", "pass", true);
        setupDataServiceResult("user2", "pass", false);
        setupDataServiceResult("user3", "pass", false);
        setupDataServiceResult("user4", "pass", true);
        _mocks.ReplayAll();
        _ourClassStub.OurAwesomeMethod(_users);
    }

    [TearDown]
    public void TearDown()
    {
        _mocks.VerifyAll();
        ConfigurationService.instance = null;
        ServiceFactory.InjectForTest(null);
    }

    private void setupStuffDoerResult(string name)
    {
        SetupResult.On(_stuffDoer).Call(_stuffDoer.DoStuff(name))
        .Return(name + " ");
    }

    [Test]
    public void OurAwesomeMethodReturnsConcatenatedUsersListWhenEnoughUsersAreValid()
    {
        setupDataServiceResult("user1", "pass", true);
        setupDataServiceResult("user2", "pass", false);
        setupDataServiceResult("user3", "pass", true);
        setupDataServiceResult("user4", "pass", true);
        setupStuffDoerResult("user1");
        setupStuffDoerResult("user3");
        setupStuffDoerResult("user4");
        _mocks.ReplayAll();
        string output = _ourClassStub.OurAwesomeMethod(_users);
        Assert.AreEqual("user1 user3 user4 ",output);
    }
}

Well, that's all the ground I want to set for unit testing, stubs and mocks. And now on to the special cases.

History

  • 30th October, 2006: Initial post

License

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

About the Author

Vishnu S. Iyengar


Member

Occupation: Web Developer
Location: India India

Other popular .NET Framework articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 29 Oct 2006
Editor: Deeksha Shenoy
Copyright 2006 by Vishnu S. Iyengar
Everything else Copyright © CodeProject, 1999-2009
Web21 | Advertise on the Code Project