Using Test Doubles with Unit Tests – PART 2

Testing

Getting back to the example, using the same repository and test that we outlined previously, we now convert that test to use a mock created by Moq instead of the repository stub. Listing below demonstrates how the code looks.

using System;
using System.Data.SqlClient;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace CodeSamples.Ch05_ClassMocking.Listing04
{
public class MockingExample
{
public void UpdateUserData()
{
var repo = ServiceLocator.Resolve();
var user = repo.GetUserData(1);
user.Name = “New Name”;
repo.SaveUserData(user);
}
}

public class User
{
public int Id { get; internal set; }
public string Name { get; set; }
}

public interface IRepository
{
User GetUserData(int pUserId);
void SaveUserData(User pUser);
}

public class Repository : IRepository
{
private string _connectionString = “connStringGoesHere”;

public User GetUserData(int pUserId)
{
var qryString = “select * from User where id= @id”;
var conn = new SqlConnection(_connectionString);
var cmd = new SqlCommand(qryString, conn);
cmd.Parameters.AddWithValue(“@id”, pUserId);
try
{
conn.Open();
var reader = cmd.ExecuteReader();
reader.Read();
var usr = new User
{
Id = (int)reader[0],
Name = reader[1].ToString()
};
reader.Close();
return usr;
}
catch (Exception)
{
//do something with the exception
}
return null;
}

public void SaveUserData(User pUser)
{
var qryString = “Update user set name = @username where id= @id”;
var conn = new SqlConnection(_connectionString);
var cmd = new SqlCommand(qryString, conn);
cmd.Parameters.AddWithValue(“@id”, pUser.Id);
cmd.Parameters.AddWithValue(“@username”, pUser.Name);
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (Exception)
{
//do something with the exception
}
}
}

public static class ServiceLocator
{
private static UnityContainer _container;

static ServiceLocator()
{
_container = new UnityContainer();
_container.RegisterInstance(_container);
_container.RegisterType();
}

public static TProxyType Resolve()
{
return _container.Resolve();
}
}

[TestClass]
public class MockingExampleTestClass
{
[TestMethod]
public void UpdateUserData_WhenCalled_SetsNewName()
{
//Arrange – section 1
var mockRepo = new Mock();
var container = ServiceLocator.Resolve();
container.RegisterInstance(mockRepo.Object);

//Arrange – section 2
var user = new User { Id = 1, Name = “ReplaceMe” };
mockRepo.Setup(mr => mr.GetUserData(1)).Returns(user);

//Act
var mockExample = new MockingExample();
mockExample.UpdateUserData();

//Assert
Assert.AreEqual(“New Name”, user.Name);
}
}
}

In this listing, you should notice that the RepositoryStub class is gone. We no longer need that hard-coded class because we are going to use the mocking library to create a mock class at runtime.
Let’s take a detailed look at the test. The first line of the Arrange – section 1 section creates a new Mock class of type IRepository using the mocking library. This line creates a new class that can mock any method or property on the given interface. We delve into how to use the mock class in just a few paragraphs, but for now let’s concentrate on one of the properties of the mock class. The mock has a property called Object, which returns a class that implements the IRepository interface. This new object can be used in place of any IRepository class anywhere in the application.
By default, this new class does nothing; it has a GetUserData method and a SaveUserData method, but there is no code in either of these methods, so if called, the GetUserData returns a null, and the SaveUserData does nothing. Later, we customize these two methods to do what we need them to do.
The Arrange – section 2 area is where we set up the mock to satisfy the needs of the current test. We first create a new User object to use for the test and we initialize the values as appropriate. We then tell the mock repository that anytime any logic anywhere in the application calls the GetUserData method with a parameter of 1, it should return the user that we created by hand. We can use this setup approach to change the logic of any method that is available on the interface.
Finally, in the Act section, we create an instance of the class we test and call the UpdateUserData method just like we did before. We Assert that the name has been changed appropriately, and our test is complete.
Using mocking, we can replace critical services in our application and make them testable with a minimum of effort or custom code.

A Second Mocking Example
As a second example, let’s continue to work with the repository, but now let’s test that the newly updated User object is actually saved back to the database. (In Listing below , all the classes are the same so for brevity, we show only the new test.)

[TestMethod]
public void UpdateUserData_WhenCalled_SavesNewDataToDB()
{
//Arrange – section 1
var mockRepo = new Mock();
var container = ServiceLocator.Resolve();
container.RegisterInstance(mockRepo.Object);

//Arrange – section 2
var user = new User { Id = 1, Name = “ReplaceMe” };
mockRepo.Setup(mr => mr.GetUserData(1)).Returns(user);

//Act
var mockExample = new MockingExample();
mockExample.UpdateUserData();

//Assert
mockRepo.Verify(mr => mr.SaveUserData(user), Times.Once());
}

Nearly the entire test is the same except for the end. The assert call has been replaced with a method call on the mock repository. This method call asserts that the SaveUserData was called exactly one time with the parameter user. If the method is not called or called more than once, this Verify call causes the test to fail. Also, if the SaveUserData method is called with any parameter other than the user we created, the test fails.
One facet of this test is that we assume the SaveUserData method works as designed. We do not test the database to make sure the data arrived, only that the method was called. Testing that SaveUserData actually updates the database is not appropriate here. That test should be put in an Integration test project and run separately.

A Third Mocking Example
The Moq library offers many other options that can make your tests more generic. In the previous example, the test looked for a specific parameter to the SaveUserData method. If we need something more generic, we can Verify that the SaveUserData method is called with any User parameter, not just the one we created (see below).

[TestClass]
public class MockingExampleTestClass
{
private Mock _mockRepo;

[TestInitialize]
public void TestInitialize()
{
_mockRepo = new Mock();
var container = ServiceLocator.Resolve();
container.RegisterInstance(_mockRepo.Object);

}

[TestMethod]
public void UpdateUserData_WhenCalled_SetsNewName()
{
//Arrange
var user = new User { Id = 1, Name = “ReplaceMe” };
_mockRepo.Setup(mr => mr.GetUserData(1)).Returns(user);

//Act
var mockExample = new MockingExample();
mockExample.UpdateUserData();

//Assert
Assert.AreEqual(“New Name”, user.Name);
}

[TestMethod]
public void UpdateUserData_WhenCalled_SavesNewDataToDB()
{
//Arrange
var user = new User { Id = 1, Name = “ReplaceMe” };
_mockRepo.Setup(mr => mr.GetUserData(It.IsAny()))
.Returns(user);

//Act
var mockExample = new MockingExample();
mockExample.UpdateUserData();

//Assert
_mockRepo.Verify(mr => mr.SaveUserData(It.IsAny()), Times.Once());
}
}

In this listing, we replace the User parameter in the Verify method with a static class called It. This class is specific to Moq (though other frameworks have similar ideas) and can take the place of any type necessary. By using this parameter, we make our Verify statement pass regardless of what parameter is passed in, as long as that parameter is of type User. I made a similar substitution in the GetUserData method, where I allow any integer to be passed in, not just a 1.
We also simplify our test setup using the TestInitialize attribute on a new method. This TestInitialize method is run once for every test in the test fixture and provides an easy, central place to do any necessary setup for each test.
One cautionary note: It is common to overuse the TestInitialize method by adding setup tasks that are appropriate only for a small subset of the tests contained in the fixture. This makes the tests brittle because the environment arranged by the TestInitialize method is used for all tests in the fixture. It also implies that anything in the TestInitialize method is required for all tests in the fixture, which can be misleading for developers working with the test fixture for the first time. It is best to put only setup code in the TestInitialize that is required for all tests in the fixture.

Learning More About Test Doubles
Using test doubles is a critical tool for a reengineering project, and we do not have enough room here to do justice to this rich testing tool. I recommend you spend some time learning Moq (or your mocking tool of choice) to be able to fully use the power of mocks in your tests. There is an excellent tutorial on Moq that should quickly get you on the road to productivity at

http://code.google.com/p/moq/wiki/QuickStart.

In this 2 series articles , we discussed how to dramatically improve the quality of your application by introducing unit tests. Test doubles, mocking, and stubs are all powerful tools that can enable a developer to fully test aspects of an application before it ever gets in front of QA.
All the procedures and techniques presented are highly dependent on the architectures built. Without dependency inversion, service location, interfaces, and an SOA, the quality that is available via unit testing is not possible.

References:
Reengineering .NET
By: Bradley Irby