Partially Mock Static Classes With Rhino Mocks

One of my favorite unit testing techniques is using partial mocks with Rhino Mocks. This allows me to substitute logic or assert function calls in my unit tests, making it simple to write concise, targeted tests. Here’s a quick example that demonstrates the basic concept.

public class Animal
{
	public void EatFood()
	{
		GetSleepy();
	}

	public virtual void GetSleepy()
	{
		throw new Exception("I break tests!");
	}
}

[TestClass]
public class AnimalTest
{
	[TestMethod]
	public void EatFood_GetsSleepy()
	{
		// Arrange
		var target = MockRepository.GeneratePartialMock<Animal>();
		target.Expect(x => x.GetSleepy());

		// Act
		target.EatFood();

		// Assert
		target.VerifyAllExpectations();
	}
}

A partial mock is essentially just a derived class that allows you to provide alternate implementations by overriding virtual methods. The problem with this is that it relies on inheritance, which makes it not feasible for static classes. The best way I’ve found to get the desired behavior is to do a bit of refactoring using interfaces and static properties. I can then inject mock objects to manipulate (or avoid) certain methods.

For this example, consider the following static class. Assume we want to write a test for the method SavePassword, but a method that it invokes, SaveToSecretLocation, contains some logic that is not conducive to unit testing.

public static class PasswordSaver
{
	public static void SavePassword(string description, string username, string password)
	{
		SaveToSecretLocation(description, username, password);
	}

	private static void SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
}

Step one is to define an interface for the method or functionality that’s causing us problems. In this case, it’s the method SaveToSecretLocation. I’m going to create an interface called ISecretLocationAccess that has a single method whose definition matches our static class’s SaveToSecretLocation.

public interface ISecretLocationAccess
{
	void SaveToSecretLocation(string description, string username, string password);
}

We obviously need a class that implements this interface in order to use it, and we have a few choices. One option is to create a new class. Speaking strictly in terms of object-oriented design principles, this will probably be your best bet. If creating a new object isn’t an option for you for some reason–the code is too spaghetti’d or something–you can convert your static class to be instantiable. I’m going to use the second method in this example.

Since I’m going to convert my static class to be instantiable, I’m going to do two things: replace static with sealed and add a private constructor. Both of these measures will prevent developers from accidentally–or intentionally–instantiating the class.

public sealed class PasswordSaver
{
	private PasswordSaver() { }
	
	public static void SavePassword(string description, string username, string password)
	{
		SaveToSecretLocation(description, username, password);
	}

	private static void SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
}

Now that our class is instantiable, we’ll implement the interface. Note that I’m implementing it explicitly so that it can co-exist with the original static method.

public sealed class PasswordSaver : ISecretLocationAccess
{
	private PasswordSaver() { }
	
	public static void SavePassword(string description, string username, string password)
	{
		SaveToSecretLocation(description, username, password);
	}

	private static void SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
	
	void ISecretLocationAccess.SaveToSecretLocation(string description, string username, string password)
	{
		throw new NotImplementedException();
	}
}

In order to re-route the logic to the interface version of the method, we need an instance of the interface. We’ll add a static property for our instance, and I’m using lazy initialization to prevent the possibility of a null reference. Note that I’ve made the property private because I don’t want developers accessing this object directly. If you don’t need the original method to remain as static, you could simply remove the static keyword from that method to implement the interface implicitly–I did that in the final solution at the bottom.

public sealed class PasswordSaver : ISecretLocationAccess
{
	private PasswordSaver() { }
	
	private static ISecretLocationAccess _secretLocationAccess;
	private static ISecretLocationAccess SecretLocationAccess 
	{ 
		get
		{
			return _secretLocationAccess ?? 
				(_secretLocationAccess = new PasswordSaver());
		}
		set
		{
			_secretLocationAccess = value;
		}
	}
	
	public static void SavePassword(string description, string username, string password)
	{
		SaveToSecretLocation(description, username, password);
	}

	private static void SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
	
	void ISecretLocationAccess.SaveToSecretLocation(string description, string username, string password)
	{
		throw new NotImplementedException();
	}
}

Now that we have access to an instance of the interface, we can relocate our logic from the original method to the interface method and re-route execution through the property.

public sealed class PasswordSaver : ISecretLocationAccess
{
	private PasswordSaver() { }
	
	private static ISecretLocationAccess _secretLocationAccess;
	private static ISecretLocationAccess SecretLocationAccess 
	{ 
		get
		{
			return _secretLocationAccess ?? 
				(_secretLocationAccess = new PasswordSaver());
		}
		set
		{
			_secretLocationAccess = value;
		}
	}
	
	public static void SavePassword(string description, string username, string password)
	{
		SaveToSecretLocation(description, username, password);
	}

	private static void SaveToSecretLocation(string description, string username, string password)
	{
		SecretLocationAccess.SaveToSecretLocation(description, username, password);
	}
	
	void ISecretLocationAccess.SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
}

At this point, our class is sufficiently refactored to support the desired testing behavior. All that’s left is for us to inject a mock object in our test. We create a mock object using Rhino Mocks, and since the property is private, we’ll use reflection to inject it. Note the use of TestCleanup to ensure we don’t leave any spent mock objects for the next test–very important!

//
// test class
//
[TestClass]
public class PasswordSaveTest
{
	private ISecretLocationAccess _mockSecretLocationAccess;
	
	[TestInitialize]
	private void TestInitialize()
	{
		_mockSecretLocationAccess = MockRepository.GenerateMock<ISecretLocationAccess>();
		InjectSecretLocationAccess(_mockSecretLocationAccess);
	}
	
	[TestCleanup]
	private void TestCleanup()
	{
		InjectSecretLocationAccess(null);
	}
	
	private void InjectSecretLocationAccess(ISecretLocationAccess secretLocationAccess)
	{
		typeof(PasswordSaver)
           .GetProperty("SecretLocationAccess", BindingFlags.NonPublic | BindingFlags.Static)
           .SetValue(null, secretLocationAccess, null);
	}
	
	[TestMethod]
	public void SavePassword_SavesPasswordToSecretLocation()
	{
		// Arrange
		const string description = "description";
		const string username = "username";
		const string password = "password";
		_mockSecretLocationAccess.Expect(x => 
			x.SaveToSecretLocation(description, username, password));
			
		// Act
		PasswordSaver.SavePassword(description, username, password);
		
		// Assert
		_mockSecretLocationAccess.VerifyAllExpectations();
	}
}
//
// final solution
//
public sealed class PasswordSaver : ISecretLocationAccess
{
	private PasswordSaver() { }
	
	private static ISecretLocationAccess _secretLocationAccess;
	private static ISecretLocationAccess SecretLocationAccess 
	{ 
		get
		{
			return _secretLocationAccess ?? 
				(_secretLocationAccess = new PasswordSaver());
		}
		set
		{
			_secretLocationAccess = value;
		}
	}
	
	public static void SavePassword(string description, string username, string password)
	{
		SecretLocationAccess.SaveToSecretLocation(description, username, password);
	}
	
	void ISecretLocationAccess.SaveToSecretLocation(string description, string username, string password)
	{
		throw new Exception("I break tests!");
	}
}

public interface ISecretLocationAccess
{
	void SaveToSecretLocation(string description, string username, string password);
}
Advertisements

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s