Additional Blogs by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
All of us know that unit tests are an essential precondition for quality software, but unit tests must meet certain conditions. One of them is that one test function should test only one thing. Another condition is that tests should not depend on each other. One of the most important conditions is that the tests must be fast and they should test the code in isolation. The idea of Mock objects is to create lightweight, controllable replacements for the real objects so that the tests' preconditions to be met. In fact there are two kind of such object - stubs and mocks.

Mock objects technique was first presented by Tim Mackinnon, Steve Freeman, and Philip Craig at XP2000 [1]. Mock objects purpose is to test code in isolation from the rest of the code; they replace the real objects with which your code under test collaborates. At this point Mock objects sound a lot of like stubs. Stubs replace real objects which are very expensive to create and manipulate. To create a stub you implement an interface where you hardcode some data or functionality. Although stubs can be helpful, they are also sometimes difficult to write and maintain. The key difference between mocks and stubs is that mocks goes beyond simply returning hardcoded data, but you set expectations about which methods of the mock object are called, in which sequence, with which parameters and so on [2].

The presence of Mock objects defines two testing types - state-based and interaction-based. State-based is the classical approach, you load the object that is to be tested, but also all other objects that are needed by it to run the test. When you load the objects you invoke the functionality to be tested (class' method) and finally you need to check the result for correctness by creating several assert statements which check the graph of objects you have loaded. The second type is the interaction-based testing - here you create a real instance for the class to be tested, but instead of creating real instances for the referenced objects you create mock instances. As a subsequent step you need to set expectations to these mock objects. The expectations indicate which methods should be called on the mocks when the test is performed. After the test is executed you can still create assert statements, but you also need to verify that the mock objects were executed according their expectations.

Here is a list of the reasons to use Mock object cited from the book "Test-Driven Development: A Practical Guide" by David Astels:
  • It helps keep the design decoupled. Using mocks help to enforce interface-centric design. Programming against a mock removes the possibility of depending on the implementation of the objects being used.
  • It checks your code's usage of another object. By setting expectations in a mock we can verify that the code we are working on properly uses the mocked interface
  • It test-drives your code from the inside out. By setting return values in the mocks, we can provide specific information to the code under development, then test that the resulting behavior is correct.
  • It makes your tests run faster.
  • It makes it easier to develop code that interacts with hardware devices, remote systems, and other problematic resources.
  • It can defer the implementation of a class.
  • It helps us test-drive components in isolation from the rest of the system. By mocking the component that the code being written has to interact with, we can focus on it in isolation. This lets us go faster, because complex interactions with other components are fully under our control.
  • It helps to test unusual, unlikely, and exceptional situations. You can easily create a mock that will return values that don't usually occur, or that will throw exceptions on demand. This allows you to easily test exception handling, even when the exception is hard to reproduce by the real code, such as the FileOutputStream class to throw IOException in a unit test.
There are two interesting open-source frameworks for Java which help creating Mock Objects. The first is called obviously "Mock Objects" [4]. Here is an example mock object created with this framework:

Here is the interface which real implementation deals with the database:
public interface IPersonMapper {
&nbsp &nbsp public void insert(Person person);
}


Here is the corresponding mock object using the "Mock Objects"  Framework API, but which was generated with "Mock Maker" Eclipse plugin [5]:
public class MockIPersonMapper implements IPersonMapper {
&nbsp &nbsp private ExpectationCounter myInsertCalls = new ExpectationCounter("com.sap.mock.IPersonMapper InsertCalls");
&nbsp &nbsp private ReturnValues myActualInsertReturnValues = new VoidReturnValues(false);
&nbsp &nbsp private ExpectationList myInsertParameter0Values = new ExpectationList("com.sap.mock.IPersonMapper com.sap.mock.Person");
&nbsp &nbsp public void setExpectedInsertCalls(int calls){
&nbsp &nbsp &nbsp &nbsp myInsertCalls.setExpected(calls);
&nbsp &nbsp }
&nbsp &nbsp public void addExpectedInsertValues(Person arg0){
&nbsp &nbsp &nbsp &nbsp myInsertParameter0Values.addExpected(arg0);
&nbsp &nbsp }
&nbsp &nbsp public void insert(Person arg0){
&nbsp &nbsp &nbsp &nbsp myInsertCalls.inc();
&nbsp &nbsp &nbsp &nbsp myInsertParameter0Values.addActual(arg0);
&nbsp &nbsp &nbsp &nbsp Object nextReturnValue = myActualInsertReturnValues.getNext();
&nbsp &nbsp &nbsp &nbsp if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof RuntimeException)
&nbsp &nbsp &nbsp &nbsp &nbsp &nbsp throw (RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException();
&nbsp &nbsp }
&nbsp &nbsp public void setupExceptionInsert(Throwable arg){
&nbsp &nbsp &nbsp &nbsp myActualInsertReturnValues.add(new ExceptionalReturnValue(arg));
&nbsp &nbsp }
&nbsp &nbsp public void verify(){
&nbsp &nbsp &nbsp &nbsp myInsertCalls.verify();
&nbsp &nbsp &nbsp &nbsp myInsertParameter0Values.verify();
&nbsp &nbsp }
}
In the test code you need to create an instance of the mock class, set the expectations, execute the code which is to be tested, and finally to validate the mock object.

The next framework for "Mock Objects" is called "Easy Mock" [6]. "Easy Mock" generates "Mock Object" dynamically through reflection. There is no need to write them or to generate code. Here is an example with the IPersonMapper interface shown above:
public void test...() {
&nbsp &nbsp Person person = ....;
&nbsp &nbsp IPersonMapper mockPersonMapper = (IPersonMapper)EasyMock.createMock(IPersonMapper.class);
&nbsp &nbsp EasyMock.expect(mockPersonMapper.insert(person));
&nbsp &nbsp EasyMock.replay(mockPersonMapper);
&nbsp &nbsp //... invoke the code to be tested
}


Mock objects technique helps us  to write focused tests that test only a single method, without side effects resulting from other objects being called from the method under test. Writing small, focused tests is a tremendous help; small tests are easy to understand and do not break when other parts of the code are changed. Mock objects complement the Test-driven development methodology.

References:
[1] [connextra.com] Endo-Testing: Unit Testing with Mock Objects
[2] [Fowler] Mocks Aren't Stubs
[3] ["JUnit in Action" book sample chapter] Testing in isolation with mock objects
[4] [mockobjects.com] MockObjects - Project Description and Goals
[5] [sourceforge.net] Mock Maker
[6] [easymock.org] EasyMock
2 Comments