devxlogo

Unit Test More Efficiently with Mock Object Alternatives

Unit Test More Efficiently with Mock Object Alternatives

hen you’re unit testing, you often want to test an individual unit of code without testing its dependencies. One common solution to this problem is to utilize the mock-object testing pattern. Though the mock-objects pattern can lead to a great working solution, many times, it is simply overkill. Extreme Programming (XP) espouses the principle: do the simplest thing that could possibly work. To this end, mock objects aren’t always the simplest thing possible. This article introduces you to an approach for testing with stubs using a pattern called “pseudo objects.”

Author’s Note: The examples in this article are provided in Java, but the patterns that are discussed are language agnostic. You can use these patterns while programming in any object-oriented language.

Testing Strategies, Mocks, and Stubs
In interaction-based unit testing, tests typically pass mock objects into methods so that the mock objects can verify the implementation of the code that is being tested. Mocks are fake objects that record the manner upon which they are operated and can verify whether they were operated on properly.

In state-based unit testing, real system objects are passed to the method that is being tested and the output of the method is asserted, but the implementation of the method is not tested. If a real system object is difficult to create within the context of the test, stubs are usually used instead. Stubs, like mock objects, are fake objects as well, but they usually have no logic or recording capability. Stubs just return canned data.

Author’s Note: For a more in-depth discussion of the difference between mock objects (also called mocks) and stubs, as well as the differences between interaction and state-based unit testing, see Martin Fowler’s recent paper “Mocks Aren’t Stubs.”

The main argument for choosing state-based unit testing over interaction-based unit testing is that interaction-based unit testing couples the tests to the implementation. I’ll show you some examples of interaction-based unit testing using mocks, then I’ll introduce you to state-based unit testing by showing you a simple pattern that will allow you to easily create stubs.

Mock Objects
The idea behind the mock object pattern is that you pass a phony object (the mock object) into a method that is being tested. The method must accept an abstract class or interface as its parameter, and the phony object must implement that interface. The test preloads the mock object with values to be used by the method or methods that the mock is passed in to. Tests also setup expected values and conditions for mock objects. As the methods are being tested, they call mock methods. The mock object records information such as the values that are passed in and the number of times each method is called. At the end of a test a verify() method is called on the mock object. This verify() method will assert whether or not all the expected actions were performed correctly, thus reporting whether the method being tested was implemented correctly. Note that mock objects are only used for testing and thus contain no production code.

Mock objects will typically implement an interface and sometimes extend an abstract class. For example, suppose you are writing an accounting system, and you have an interface called Account. To create a mock, you would create a class called AccountMock that implements the Account interface. A mock could be an extension of a concrete class, but this is rare and generally regarded as bad design.

Author’s Note: I prefer AccountMock over MockAccount, because it groups alphabetically next to Account in most IDEs and makes life simpler if you are using code-completion or if you choose to keep your test code and your production code in the same location.

Here’s a simple example of an Account interface:

 public interface Account{	public void addEntry( Date date, double amount );	public List getEntriesFor( int month, int year );	public double getBalance();	.	.	.}

The mock object for this interface would have the following methods:

public class AccountMock implements Account{      public void addEntry(AccountEntry arg0);   public void setupExceptionAddEntry(Throwable arg);   public void setExpectedAddEntryCalls(int calls);   public void addExpectedAddEntryValues(AccountEntry arg0);   public List getEntriesFor(int arg0, int arg1);   public void setExpectedGetEntriesForCalls(int calls);   public void addExpectedGetEntriesForValues(int arg0, int arg1);   public void setupExceptionGetEntriesFor(Throwable arg);   public void setupGetEntriesFor(List arg);   public double getBalance();   public void setupExceptionGetBalance(Throwable arg);   public void setupGetBalance(double arg);    public void setExpectedGetBalanceCalls(int calls);   .   .   .   public void verify();}

Now, suppose you have a ReportFactory class and you want to write a method called createMonthlyReport that takes an account, a month, and a year, and produces a report. If you are an interaction-based unit tester, you would opt for using a mock Account object. If you are a state-based unit tester, you would opt for a real Account object in order to decouple your test from your implementation. If the Account object required too much context, making it difficult to create in the testing environment, you would create a stub.

Here’s an interaction-based unit test for the createMonthlyReport object that uses a mock:

public void testCreateMonthlyReport(){	//setup	AccountMock account = new AccountMock();	account.setExpectedGetBalanceCalls(1);	account.setupGetBalance(55.45);	// ... etc	int month = 2;	int year = 1999;	//execute	Report actualReport = 		ReportFactory.createMonthlyReport( month, year, account ); 	//verify	assertEquals( expectedReport, actualReport );	account.verify();}

As you can see, the setup section creates an AccountMock and populates it with expected values and values that it should return when certain methods are called. The execute section calls the method being tested and passes in the mock as an argument. The verify section checks that the method has returned the correct return value. The verify method is then called upon to ensure that the correct calls were made to the mock object.

Here is a state-based unit test for the same method, using a stub:

public void testCreateMonthlyReport() {	//setup	Account account = new Account() {		public double getBalance() {			return 5.12;		}		// ... etc	};	int month = 2;	int year = 1999;	//execute	Report actualReport =		ReportFactory.createMonthlyReport( month, year, account );	//verify	assertEquals( expectedReport, actualReport );}

This time, the setup section creates an Account object by using an anonymous inner class and implementing the entire Account interface. There are several options that are better approaches if the Account interface is rather large. One is to use a regular inner class or a top-level class for your Account stub. This may be used by more than one test, so this isn’t so bad. Another method is to use a pattern called ObjectMother. This returns a real object with default values and allows you to change those values using setter methods. This works well for mutable objects, but not so well for immutable objects. A third way is to use pseudo objects, which are explained next.

Pseudo Objects
Here’s a scenario where mock objects may be a bit of overkill. Suppose you’re going to test a method, and the path you’re going to test shouldn’t reference a particular object that is passed into the method as an argument. For example, say you want to test the boundary conditions in the following method:

public static Report createMonthlyReport(int month, int year, Account account) {	if (month  12) {		throw new IllegalArgumentException();	}	if (year  getCurrentYear()) {		throw new IllegalArgumentException();	}	.	.	.}

Your test needs to pass in values that will cause the IllegalArgumentException to be thrown. This path through the createMonthlyReport() method will not access the Account object that is passed in; in fact, you want to ensure that the method doesn’t reference the Account object. You could pass in a mock object, but you would have to do a bit of setup in your test, as shown below:

public void testMonthCannotBeNegative() {	AccountMock account = new AccountMock();	account.setExpectedGetBalanceCalls(0);	account.setExpectedAddEntryCalls(0);	account.setExpectedGetEntriesForCalls(0);	// .. etc	int month = -1;	int year = 1999;	try {		ReportFactory.createMonthlyReport(month, year, account);		fail("Exception should have been thrown on negative month");	} catch (IllegalArgumentException e) {		;	}}

This test ensures that none of the methods on the Account object is called, and it does this by setting the “expected calls” values for each method on the mock to 0.

Now, take a look at a simpler technique that accomplishes the same thing. Instead of creating a mock object that implements the Account interface, create what I call a “pseudo object.” A pseudo object does nothing more than extend an interface and throw exceptions on any method call. In the case of Account, the AccountPseudo would look like this:

public class AccountPseudo implements Account {	public void addEntry(Date date, double amount) {		throw new PseudoError();	}	public List getEntriesFor(int month, int year) {		throw new PseudoError();	}	public double getBalance() {		throw new PseudoError();	}}public class PseudoError extends RuntimeException {}

As you can see, this is definitely not a mock object. There are no methods defined above and beyond what is available on the interface. The pseudo doesn’t remember any object state or verify anything during or after test execution; it simply throws an exception on any method call. Now, this is not necessarily a stub, because it is not providing any canned values that a test may use. But, as you’ll see shortly, it is a very convenient class to extend when you need a stub.

If you wanted to use a pseudo instead of the mock, your test code would look something like this:

public void testMonthCannotBeNegative () {	Account account = new AccountPseudo();	int month = -1;	int year = 1999;	try {		ReportFactory.createMonthlyReport(month, year, account);		fail("Exception should have been thrown on negative month");	} catch (IllegalArgumentException e) {		;	}}

That’s a bit simpler, isn’t it? By virtue of their construction, pseudos provide an inherent error-producing mechanism. By looking at the test, you can immediately tell that the test expects that the createMonthlyReport method will not operate on the Account object. How? Because if any code within the createMonthlyReport method calls any of the methods on the AccountPseudo, an exception will be thrown and the test will fail.

But that’s not the only thing you can do with pseudo objects. As I mentioned earlier, you can also use a pseudo object to create a stub that returns canned data by extending the pseudo class and overriding its methods.

This unit test creates a stub by extending the AccountPseudo class and providing only the functionality needed for the test:

public void testCreateMonthlyReport() {	//setup	Account account = new AccountPseudo() {		public double getBalance() {			return 55.5;		}	};	int month = 2;	int year = 1999;	//execute	Report actualReport = 		ReportFactory.createMonthlyReport(month, year, account);	//test	Report expectedReport = createExpectedReport();	assertEquals(expectedReport, actualReport);}

The stub is created by overriding AccountPseudo using an anonymous inner class that exists directly in the test method. You could use a named inner class or a top-level class just the same, but using an anonymous inner class is more compact. In fact, the inline stub makes the test more self-contained and explicit.

Another advantage to using inline stubs is that you can quickly detect when your test, and in turn your code, is doing too much. If your anonymous inner class starts growing too big, that is a pretty good indication that you should consider doing a little refactoring in order to separate functional responsibility of the method that you’re testing.

For example, a method may be responsible for creating objects and then acting on those objects. These responsibilities could be split into two fine-grain methods, one factory method and another that operates on the objects. Fine-grain methods are more easily tested and are more apt to be reused. This is generally considered better from a design perspective.

You can use both stubs and mocks in your code. I lean more towards state-based unit-testing and thus use stubs almost exclusively. There are many people who use mock object libraries to create their stubs. I find it easier to use stubs that extend from a pseudo class. This also deters me from using the mock methods to test the implementation details of my methods. If I am working on code that uses mocks and it is simpler to use stubs, I usually do “replace mock with stub” refactoring to get rid of the mock.

If you are doing a mixture of interaction and state-based unit testing, one thing to remember is that mocks can actually extend pseudos the same way that stubs do. This strategy works well if you are incrementally developing your own custom mock objects, and only mocking methods that are on the interface as they are needed by your tests. But, this is not very beneficial if you are auto-generating your mock objects with a code-generation tool.

Passing in Mocks and Pseudos
It’s often difficult to test methods that are responsible for their own object creation. If a method is responsible for creating the objects that it uses, then the method cannot be tested with substituted objects such as mock and stubs. The method must be refactored to separate the creation of the objects from the operations on those objects.

There are two primary refactorings that I have seen used to solve this problem. They both attempt to separate creational and behavioral functionality. This section starts with a simple example, and then you’ll walk through both refactoring strategies so that you can see the resulting code and corresponding unit test.

The first refactoring strategy is documented in an article on IBM DeveloperWorks by Alex Chaffee and William Pietri. In their article’s example, you have a run() method that creates and uses a View object:

public class Application {	public void run() {		View v = new View();		v.display();	}}

The article demonstrates testing the run method by refactoring the code through extracting a factory method:

public class Application {	public void run() {		View v = createView();		v.display();	}	protected View createView() {		return new View();	}}

Next, you can write a testcase that overrides the createView() method and passes in a mock or a stub instead.

class ApplicationTest extends TestCase {	private MockView mockView = new MockView();	public void testApplication() {		Application a = new Application() {			protected View createView() {				return mockView;			}		};		a.run();		mockView.validate();	}}

When the run() method is called, the MockView is used instead of the real View object. This allows you to substitute mock objects for real objects, but not without a cost:

  • You can’t make your class final because the test class needs to override it.
  • You have to create a new factory method for every single object that the method under test needs to use.

A second and slightly simpler way to test this is to start with a simple refactoring for the original Application class:

public class Application {	public void run() {		run(new View());	}	// package private for testing only	void run(View view) {		view.display();	}}

Make the public run method a pass-through to a package-private run method. The public run method will still be called by production code in the same exact way, though it doesn’t do anything besides create the real View object and pass it in to the package-private run method. The package-private run method operates on the objects, but is free of creational responsibilities. This method remains package-private so that the test class can access it.

Here is the test:

class ApplicationTest extends TestCase {	private MockView mockView = new MockView();	public void testApplication() {		Application application = new Application();		application.run(mockView);		mockView.validate();	}}

Now, all you have to do is test the package-private run method that takes a parameter. The test is focused on just the behavioral aspect of the class. You don’t have to override anything, and you can make the class final if you so desire. If the run method needs more than one object, simply make the public run method create multiple objects, and make the “testable” run method take multiple arguments. There is no need to test the run method that takes no parameters because it has no behavioral responsibilities that need testing.

Another method of accomplishing the same thing is to make the client code responsible for creating the objects. You can achieve this by creating all the necessary objects in the client code and then passing them in through either the constructor or through setter methods. This is often referred to as dependency injection. There are dependency injection frameworks that make this job easy. I have written a two-part article on a dependency injection framework called Spring. The articles can be found here on DevX:

Spring: Creating Objects So You Don’t Have To
Simplify Your Web App Development Using the Spring MVC Framework

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist