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 < 1 || month > 12) {		throw new IllegalArgumentException();	}	if (year < 1975 || 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

devx-admin

devx-admin

Share the Post:
Chinese 5G Limitation

Germany Considers Limiting Chinese 5G Tech

A recent report has put forth the possibility that Germany’s Federal Ministry of the Interior and Community may consider limiting the use of Chinese 5G

Modern Warfare

The Barak Tank is Transforming Modern Warfare

The Barak tank is a groundbreaking addition to the Israeli Defense Forces’ arsenal, significantly enhancing their combat capabilities. This AI-powered military vehicle is expected to

AI Cheating Growth

AI Plagiarism Challenges Shake Academic Integrity

As generative AI technologies like ChatGPT become increasingly prevalent among students and raise concerns about widespread cheating, prominent universities have halted their use of AI

US Commitment

US Approves Sustainable Battery Research

The US Department of Energy has revealed a $325 million commitment in the research of innovative battery types, designed to enable solar and wind power

Netanyahu Musk AI

Netanyahu and Musk Discuss AI Future

On September 22, 2023, Israeli Prime Minister Benjamin Netanyahu met with entrepreneur Elon Musk in San Francisco prior to attending the United Nations. In a

Chinese 5G Limitation

Germany Considers Limiting Chinese 5G Tech

A recent report has put forth the possibility that Germany’s Federal Ministry of the Interior and Community may consider limiting the use of Chinese 5G technology by local network providers

Modern Warfare

The Barak Tank is Transforming Modern Warfare

The Barak tank is a groundbreaking addition to the Israeli Defense Forces’ arsenal, significantly enhancing their combat capabilities. This AI-powered military vehicle is expected to transform the way modern warfare

AI Cheating Growth

AI Plagiarism Challenges Shake Academic Integrity

As generative AI technologies like ChatGPT become increasingly prevalent among students and raise concerns about widespread cheating, prominent universities have halted their use of AI detection software, such as Turnitin’s

US Commitment

US Approves Sustainable Battery Research

The US Department of Energy has revealed a $325 million commitment in the research of innovative battery types, designed to enable solar and wind power as continuous, 24-hour energy sources.

Netanyahu Musk AI

Netanyahu and Musk Discuss AI Future

On September 22, 2023, Israeli Prime Minister Benjamin Netanyahu met with entrepreneur Elon Musk in San Francisco prior to attending the United Nations. In a live-streamed discussion, Netanyahu lauded Musk

Urban Gardening

Creating Thriving Cities Through Urban Gardening

The rising popularity of urban gardening is receiving increased recognition for its numerous advantages, as demonstrated in a recent study featured in the Environmental Research Letters journal. Carried out by

What You Need to Know About Cloud Security Strategies

What You Need to Know About Cloud Security Strategies

Today, many businesses are adopting cloud computing services. As a result, it’s important to recognize that security measures for data in the cloud are different from those in traditional on-premises

Romanian Energy Security

Eastern Europe is Achieving Energy Security

Canada and Romania have solidified their commitment to energy security and independence from Russian energy exports by signing a $3-billion export development agreement. The deal is centered on constructing two

Seamless Integration

Unlocking Seamless Smart Home Integration

The vision of an intelligently organized and interconnected smart home that conserves time, energy, and resources has long been desired by many homeowners. However, this aspiration has often been hindered

New Algorithm

MicroAlgo’s Groundbreaking Algorithm

MicroAlgo Inc. has revealed the creation of a knowledge-augmented backtracking search algorithm, developed through extensive research in evolutionary computational techniques. The algorithm is designed to boost problem-solving effectiveness, precision, and

Poland Energy Future

Westinghouse Builds Polish Power Plant

Westinghouse Electric Company and Bechtel have come together to establish a formal partnership in order to design and construct Poland’s inaugural nuclear power plant at the Lubiatowo-Kopalino site in Pomerania.

EV Labor Market

EV Industry Hurting For Skilled Labor

The United Auto Workers strike has highlighted the anticipated change towards a future dominated by electric vehicles (EVs), a shift which numerous people think will result in job losses. However,

Soaring EV Quotas

Soaring EV Quotas Spark Battle Against Time

Automakers are still expected to meet stringent electric vehicle (EV) sales quotas, despite the delayed ban on new petrol and diesel cars. Starting January 2023, more than one-fifth of automobiles

Affordable Electric Revolution

Tesla Rivals Make Bold Moves

Tesla, a name synonymous with EVs, has consistently been at the forefront of the automotive industry’s electric revolution. The products that Elon Musk has developed are at the forefront because

Sunsets' Technique

Inside the Climate Battle: Make Sunsets’ Technique

On February 12, 2023, Luke Iseman and Andrew Song from the solar geoengineering firm Make Sunsets showcased their technique for injecting sulfur dioxide (SO₂) into the stratosphere as a means

AI Adherence Prediction

AI Algorithm Predicts Treatment Adherence

Swoop, a prominent consumer health data company, has unveiled a cutting-edge algorithm capable of predicting adherence to treatment in people with Multiple Sclerosis (MS) and other health conditions. Utilizing artificial

Personalized UX

Here’s Why You Need to Use JavaScript and Cookies

In today’s increasingly digital world, websites often rely on JavaScript and cookies to provide users with a more seamless and personalized browsing experience. These key components allow websites to display

Geoengineering Methods

Scientists Dimming the Sun: It’s a Good Thing

Scientists at the University of Bern have been exploring geoengineering methods that could potentially slow down the melting of the West Antarctic ice sheet by reducing sunlight exposure. Among these

why startups succeed

The Top Reasons Why Startups Succeed

Everyone hears the stories. Apple was started in a garage. Musk slept in a rented office space while he was creating PayPal with his brother. Facebook was coded by a

Bold Evolution

Intel’s Bold Comeback

Intel, a leading figure in the semiconductor industry, has underperformed in the stock market over the past five years, with shares dropping by 4% as opposed to the 176% return

Semiconductor market

Semiconductor Slump: Rebound on the Horizon

In recent years, the semiconductor sector has faced a slump due to decreasing PC and smartphone sales, especially in 2022 and 2023. Nonetheless, as 2024 approaches, the industry seems to

Elevated Content Deals

Elevate Your Content Creation with Amazing Deals

The latest Tech Deals cater to creators of different levels and budgets, featuring a variety of computer accessories and tools designed specifically for content creation. Enhance your technological setup with

Learn Web Security

An Easy Way to Learn Web Security

The Web Security Academy has recently introduced new educational courses designed to offer a comprehensible and straightforward journey through the intricate realm of web security. These carefully designed learning courses