C# Corner

Use Mocking Frameworks to Improve Code Quality

In today's agile/TDD world, mocking has become an important tool in the development of a good, testable design. It's important to understand your mocking framework to make sure you can maximize its benefit. But before I talk about mocking frameworks, I want to review a little bit about subclassing in .NET. Even if you're familiar with subclassing, inheritance and polymorphism, I encourage you to read on.

As I'm sure many of you know, to override a method is to create a subclass of an object and change the behavior of a method. A simple example is an interest calculator. One class may define the calculation using simple interest (I = P*r*t). You may want a new design that uses compound interest (A = P(1+r)n). Let's see how this would look in code:

public class SimpleInterestCalculator
{
	public double Principal { get; set; }
	public double InterestRate { get; set; }
	public double Time { get; set; }

	public virtual double CalculateInterestAmount()
	{
		return Principal*InterestRate*Time;
	}
}

Note that we've made CalculateInterestAmount() virtual. This allows us to override (replace) the behavior of that method in a subclass. We'll do just that in a subclass that handles compound interest calculations:

public class CompoundInterestCalculator : SimpleInterestCalculator
{
	public override double CalculateInterestAmount()
	{
		var newPrincipal = Principal*Math.Pow((1 + InterestRate), Time);
		return newPrincipal - Principal;
	}
}

The key to this is having your members, be it methods, properties, delegates or events, marked as virtual so you can change (override) their behavior.

Of course, you don't always have to change the behavior. Suppose you simply want to make a log of whenever the CaclulateInterestAmount method is called? Sure, you could add that to the SimpleInterestCalculator class, but then you're hard-coding your logging implementation into the class. Instead, we'll take advantage of overriding and create a subclass. For this example, we'll use log4net to do our logging:

public class LoggedSimpleInterestCalculator : SimpleInterestCalculator
{
	private readonly ILog logger = LogManager.GetLogger("interestLogger");

	public override double CalculateInterestAmount()
	{
		logger.DebugFormat("Calculating Interest: P={0}, i={1}, t={2}",
Principal, InterestRate, Time);
		var interest = base.CalculateInterestAmount();
		logger.DebugFormat("Interest Calculated: {0}", interest);

		return interest;
	}
}

First we log that we're about to call the interest calculation method. We then call the base implementation, and then log the result before returning it to the caller. Now your application can use the LoggedSimpleInterestCalculator whenever it wants to have a simple debugging log of the interest calculation.

Tracking Method Calls
The LoggedSimpleInterestCalculator showed how we can add additional behavior without altering the contract of the class. This means we could get crazy and do stuff like tracking what time a method was called, how often it was called -- or even if it was called at all. This type of stuff could come in handy during testing.

Let's imagine a "Bank" class that uses our SimpleInterestCalculator. Every night, the bank's "RunProcessing" method is called to calculate interest on the account only if the principal is more than $500. It's the typical, goofy scenario you see in short demo code:

public class Bank
{
	private readonly SimpleInterestCalculator interestCalculator;

	public Bank(SimpleInterestCalculator interestCalculator)
	{
		this.interestCalculator = interestCalculator;
	}

	public void RunProcessing()
	{
		if( interestCalculator.Principal > 500)
		{
			var interest = 
interestCalculator.CalculateInterestAmount();
			// do something with interest
		}
	}
}

When it comes time to test my Bank class, I can take advantage of subclassing and create my own SimpleInterestCalculator, which will keep track of whether the CalculateInterestAmount was called:

public class TesterInterestCalculator : SimpleInterestCalculator
{
	public bool CalculateWasCalled { get; set; }

	public override double CalculateInterestAmount()
	{
		CalculateWasCalled = true;
		return base.CalculateInterestAmount();
	}
}

And here's how we can use this to test the "Bank" class:

[TestMethod]
public void DontCalcInterestIfAmountLessThan500()
{
	var calculator = new TesterInterestCalculator();
	calculator.Principal = 499.99;
	calculator.InterestRate = 0.05;
	calculator.Time = 2;
	var bank = new Bank(calculator);

	bank.RunProcessing();
	Assert.IsFalse(calculator.CalculateWasCalled);
}

[TestMethod]
public void CalcInterestIfAmountOver500()
{
	var calculator = new TesterInterestCalculator();
	calculator.Principal = 500.01;
	calculator.InterestRate = 0.05;
	calculator.Time = 2;
	var bank = new Bank(calculator);

	bank.RunProcessing();
	Assert.IsTrue(calculator.CalculateWasCalled);
}

Mocking Frameworks to the Rescue
In this example, we had one method to track. It wasn't too much effort to subclass it and keep track of whether that one method was called or not. In the real world, this approach would be too unwieldy to maintain. That's where mocking frameworks come in.

Most mocking frameworks automate what we just did. You tell them you want to create a mock of a specific type. In the background, the framework will actually create a subclass of your type and track when methods are called (and how many times). Some even allow you to define a specific return value from a method -- thus allowing you to totally bypass the call to the base class method. This is handy in situations where calling the base method may take a long time. You can use the mocking framework to simply return a canned response.

Do all mocking frameworks work this way? No. Most of the free ones (like Rhino.Mocks or NMock) use this method of overriding virtual members. Because of this, they are limited to only members that are virtual -- you can't mock static methods (since they aren't virtual) nor do anything with sealed classes as they can't be subclassed. Other frameworks (like TypeMock) utilize the .NET profiling API to do their interception and don't have these limitations. Since I prefer to avoid static methods and sealed classes, I do most of my mocking with Rhino.Mocks and it handles everything I need.

How would you use a mocking framework like we did above in our tests? Here are our tests rewritten with Rhino.Mocks and without the need to create a subclass to track method calls ourselves:

[TestMethod]
public void DontCalcInterestIfAmountLessThan500()
{
	var calculator =
MockRepository.GenerateMock<SimpleInterestCalculator>();
	calculator.Principal = 499.99;
	calculator.InterestRate = 0.05;
	calculator.Time = 2;

	var bank = new Bank(calculator);

	bank.RunProcessing();
	
	calculator.AssertWasNotCalled(c => c.CalculateInterestAmount());
}

[TestMethod]
public void CalcInterestIfAmountOver500()
{
	var calculator =
MockRepository.GenerateMock<SimpleInterestCalculator>();
	calculator.Principal = 500.01;
	calculator.InterestRate = 0.05;
	calculator.Time = 2;

	var bank = new Bank(calculator);

	bank.RunProcessing();

	calculator.AssertWasCalled(c => c.CalculateInterestAmount());
}

Very nice! No separate class to maintain. The mocking framework can track all virtual method calls and you can make assertions that those calls were, or were not, made.

Mocking frameworks are another tool in your developers toolbox that you can use to enhance the quality of your software (via testing), while reducing the amount of test code you need to write (via mocking). Play around with some of the free mocking frameworks and find one that fits for you.

About the Author

Patrick Steele is a senior .NET developer with Billhighway in Troy, Mich. A recognized expert on the Microsoft .NET Framework, he’s a former Microsoft MVP award winner and a presenter at conferences and user group meetings.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube