Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Unit Test More Efficiently with Mock Object Alternatives : Page 2

The mock-object testing pattern has commonly been used to test an individual unit of code without testing its dependencies. While this pattern works well for interaction-based testing, it can be overkill for state-based testing. Learn how to streamline your unit-testing using stubs and the pseudo-objects testing pattern.


advertisement
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.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap