Let's say you've got a static or sealed class, or a class with non-virtual members that your code needs to use. You need to unit test your code, but you can't get an instance of this class and/or you can't mock it.
There are two ways to go:
- Buy a tool that let's you mock things like this, such as Typemock Isolator or the newly announced Telerik JustMock.
- Code around it.
Buy it
I've already compared Typemock Isolator ($799) to RhinoMocks (free). Typemock Isolator works as advertised. The biggest issue is the price. It's hard enough to get management approval for introducing external tools. But when buy-in literally means BUY in, it's even harder.
Build it
Don't despair if you're stuck with "code around it" and using free mocking tools! If you can change the code that is static, sealed, private, non-virtual, etc. fix it that way. That's the best thing.
But if it's in a legacy assembly you have to use, and you can't just change the original code, use this simple technique.
Here's the untestable class we need to use. It's static, so we can't mock an instance of it with Rhino Mocks.
public static class StaticSendOrderClass
{
public static void SendOrderToAccountsPayable(Order order)
{
// Pretend code here...
}
}
Here's the new code you're working on that uses this static class to process an order:
public class OrderProcessor
{
public void ProcessTheOrder(Order order, bool useStaticClass)
{
if (useStaticClass == true)
{
StaticSendOrderClass.SendOrderToAccountsPayable(order);
}
else
{
// Notify accounts payable some other way
}
}
}
First, we need to create an interface with just the functionality we need from the untestable class. Name the class and method(s) whatever makes sense for your app. You're not tied to the legacy code naming convention; this is your code now.
public interface INotifyAccountsPayable
{
void SendOrder(Order order);
}
Next, create a new class to implement this interface and wrap the untestable code. In the method calls in the new class, turn around and delegate the call to the untestable class.
public class NotifyAccountsPayable : INotifyAccountsPayable
{
public void SendOrder(Order order)
{
StaticSendOrderClass.SendOrderToAccountsPayable(order);
}
}
Now inject the new interface into the constructor of your new class as an argument. You'll also want to create a private field and set its value to the instance argument in the constructor. Now you can swap out your old code that calls the untestable code, and instead use your new private field.
public class OrderProcessor
{
private readonly INotifyAccountsPayable _notifyAccountsPayable;
public OrderProcessor(INotifyAccountsPayable notifyAccountsPayable)
{
_notifyAccountsPayable = notifyAccountsPayable;
}
public void ProcessTheOrder(Order order, bool useStaticClass)
{
if (useStaticClass == true)
{
_notifyAccountsPayable.SendOrder(order);
}
else
{
// Notify accounts payable some other way
}
}
}
Now you've got a unit testable OrderProcessor
class! Mock the INotifyAccountsPayable
interface with Rhino Mocks and inject that mocked dependency into your class under test. Then you can verify the call was made as expected.
[Test]
public void Should_call_untestable_code_when_useStaticClass_is_set_to_true()
{
// Arrange
var mockNotifyAccountsPayable = MockRepository.GenerateMock<INotifyAccountsPayable>();
var orderProcessor = new OrderProcessor(mockNotifyAccountsPayable);
var stubOrder = MockRepository.GenerateStub<Order>();
var useStaticClass = true;
// Act
orderProcessor.ProcessTheOrder(stubOrder, useStaticClass);
// Assert
mockNotifyAccountsPayable.AssertWasCalled(x => x.SendOrder(stubOrder));
}
Please note this is not testing the internals of the untestable, static code. But you don't need to worry about unit testing the internals of that code. You don't own that code. Testing that is really an integration test, not a unit test. Using this approach, you isolate your new code from an external dependency and keep all your new code testable.