Sirisha Ancha Team : Web Development Tags : Technology Web Development

Mocking Objects for Unit Testing

Sirisha Ancha Team : Web Development Tags : Technology Web Development

Unit Testing

Unit testing is necessary where complex code needs to be tested in small blocks. On many occasions I have been in situations where unit test code coverage was considered to be "business critical", but as a developer it is necessary to decide which scenarios justify unit tests, and which don't. Done right, unit tests become powerful tools, ensuring the application remains maintainable for years to come. Unit tests for complex scenarios form the building blocks for integration tests. If an integration test fails these unit tests help to infer the real cause.

How important is the code coverage just for the sake of it?

Isn't it just meaningless to write unit tests for code that we know works as expected? Unit tests for complex functionality make more sense.

Design Patterns & Mocking Objects

So we're ready to write some tests. First thing's first; what do we do with all the dependent classes needed to test the functionality? How do we decide what these dependent classes could return so we can test the code that calls on them. Wouldn't it be even better if we could return all possible scenarios? This is where mocking these dependent classes will come into play.

Dependency Injection

It would be ideal if we could mock the actual dependent classes, but in C# we can only mock interfaces instead of classes. These interfaces are mocked up and the methods are set to return the required results to test the code. Mocked objects are then passed into the classes as dependencies (usually in the constructor).

Example:

We need to test the MemberExists() method of the MemberProvider class, which is dependent on the MemberRequest class. Here we pass the interface IMemberRequest as a constructor parameter.

public class MemberProvider
{
        private IMemberRequest MemberRequest { get; set; }
        public MemberProvider(IMemberRequest memberRequest)
        {
            MemberRequest = memberRequest;
        }
        public bool MemberExists(int memberId)
        {
            var product = MemberRequest.GetMember(memberId);
            if (product == null)
                return false;
            return product;
        }
}

For the unit test we create a mock object for MemberRequest.

[TestClass]
public class MemberProviderTest
{
    private Mock<IMemberRequest> MemberRequest { get; set; }
    
    [TestInitialize]
    public void Initialise()
    {
         MemberRequest = new Mock<IMemberRequest>();      
    }

Test Scenario 1

The first unit test case scenario is when no member is found. In this case MemberRequest object is set to return null when GetMember is called. When the test is run the MemberExists method is called the value returned by the mock object is null. So the method must return false.

    [TestClass]
    [TestMethod]    
    public void Member_NoMemberProductExists()
    {
        MemberRequest.Setup(s => s.GetMember(It.IsAny<int>()).Returns(null);
        var memberProvider = new MemberProvider(MemberRequest.Object);
        var result = memberProvider.MemberExists(2);
        Assert.IsFalse(result);
     }

Test Scenario 2

The next unit test case scenario is when a member is found. MemberRequest object is set to return a Member object when GetMember is called. When the test is run the MemberExists method is called the value returned by the mock object is not null. So the method must return true.

    [TestClass]
    [TestMethod]    
    public void Member_NoMemberProductExists()
    {
        MemberRequest.Setup(s => s.GetMember(It.IsAny<int>()).Returns(null);
        var memberProvider = new MemberProvider(MemberRequest.Object);
        var result = memberProvider.MemberExists(2);
        Assert.IsFalse(true);
     }
}

The above examples show how basic functionality is tested. This can obviously be extended to test any feature that we want. Mocking objects comes in very handy when writing tests. Mocks allow us to instantiate and populate objects not usually accessible in user code. The more complex the code, the more useful the tests become.