Simple test implementation pattern for mock a method in the SUT object. The pattern is interesting when you would refactor a method in legacy system. Before you can start with the refactoring stuff you must write a test for the method with a high coverage (Refactoring details see Martin Fowlers great book "Refactoring - Improving the Design of Existing Code").
The idea is to test only the legacy method you would like to refactor (to get quick a high coverage), there for we want mock all method calls which will be invoked from this method. Normal mocking frameworks like EasyMock or JMock for Java can create mock objects for interfaces and classes. This pattern shows how this tools can be used for mock an internal method in the SUT object.
The idea, overwrite all method which should be mocked in the SUT object and delegate the calls to a mock object of the same type as the SUT object.
Example setup code with JMock as mocking framework:
...
@Before
public void setUp(){
...
// Create mock object for the SUT class, this mock will be used to mock methods in the SUT
mockFrameworkCommandProvider = context.mock(FrameworkCommandProvider.class);
// Create the SUT object of type FrameworkCommandProvider
frameworkCommandProvider = new FrameworkCommandProvider(mockFramework) {
/**
* Mock the SUT Method getBundleFromToken(...)
* All method calls will delegate to a mock
* of the same type then the SUT object
*/
@Override
protected AbstractBundle getBundleFromToken(CommandInterpreter intp, String token, boolean error) {
// delegate the call to a mock object.
return mockFrameworkCommandProvider.getBundleFromToken(intp, token, error);
}
};
}
...
The code with a example test which expected a mock method call:
...
@Test
public void _bundle_OneArgumentsAndBundleNotFoundByToken() throws Exception {
final String firstToken = "myBundleName[1.0.0]";
// Setup mock context
context.checking(new Expectations() {{
// one argument
one (mockCommandInterpreter).nextArgument(); will(returnValue(firstToken));
// next calles return null
allowing (mockCommandInterpreter).nextArgument(); will(returnValue(null));
// getBundleFromToken returns null
one (mockFrameworkCommandProvider).getBundleFromToken(mockCommandInterpreter, firstToken, true);
will(returnValue(null));
}});
// invoke the _bundle() method
frameworkCommandProvider._bundle(mockCommandInterpreter);
}
...
Downsides:
- Much code for the test setup to overwrite all the methods which should be mocked in the SUT object
- Only protected, package and public methods can be mocked by this approach
- Private methods can't be mock with this pattern
- In the test code it's not clear which SUT methods are mocked and which not
Here the whole example test code:
package org.eclipse.osgi.framework.internal.core;
import static org.junit.Assert.*;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(JMock.class)
public class FrameworkCommandProviderTest {
// Create mock context and activate mocking for java classes.
Mockery context = new JUnit4Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
InternalSystemBundle mockInternalSystemBundle;
Framework mockFramework;
CommandInterpreter mockCommandInterpreter;
FrameworkCommandProvider mockFrameworkCommandProvider;
FrameworkCommandProvider frameworkCommandProvider;
@Before
public void setUp(){
// Create mock object for system bundle
mockInternalSystemBundle = context.mock(InternalSystemBundle.class);
// Mocking the getContext Method, returns always null
context.checking(new Expectations() {{
allowing (mockInternalSystemBundle).getContext(); will(returnValue(null));
}});
// Create mock object for framework
mockFramework = context.mock(Framework.class);
// Set the mock object system bundle into the framework mock
mockFramework.systemBundle = mockInternalSystemBundle;
// Create mock object for command interpreter
mockCommandInterpreter = context.mock(CommandInterpreter.class);
// Create mock object for the SUT class, this mock will be used to mock methods in the SUT
mockFrameworkCommandProvider = context.mock(FrameworkCommandProvider.class);
// Create the SUT object of type FrameworkCommandProvider
frameworkCommandProvider = new FrameworkCommandProvider(mockFramework) {
/**
* Mock the SUT Method getBundleFromToken(...)
* All method calls will delegate to a mock
* of the same type then the SUT object
*/
@Override
protected AbstractBundle getBundleFromToken(CommandInterpreter intp, String token, boolean error) {
// delegate the call to a mock object.
return mockFrameworkCommandProvider.getBundleFromToken(intp, token, error);
}
};
}
@Test
public void _bundle_OneArgumentsAndBundleNotFoundByToken() throws Exception {
final String firstToken = "myBundleName[1.0.0]";
// Setup mock context
context.checking(new Expectations() {{
// one argument
one (mockCommandInterpreter).nextArgument(); will(returnValue(firstToken));
// next calles return null
allowing (mockCommandInterpreter).nextArgument(); will(returnValue(null));
// getBundleFromToken returns null
one (mockFrameworkCommandProvider).getBundleFromToken(mockCommandInterpreter, firstToken, true);
will(returnValue(null));
}});
// invoke the _bundle() method
frameworkCommandProvider._bundle(mockCommandInterpreter);
}
}
Run the Example Test:
To run the test you need the equinox osgi implementation as SUT, you can get the equinox source from the eclipse CVS ("pserver:dev.eclipse.org/cvsroot/rt/org.eclipse.equinox/framework/bundles/org.eclipse.osgi/").
Links: