Mastering Mock Objects: Advanced Unit Testing For Java
Mastering Mock Objects: Advanced Unit Testing For Java
Denilson Nastacio
RTP
Your Host
Concepts
Intercepting methods Declaring expectations Tools
3
Agenda
Code logic
Tests whether isolated methods respect their exposed contract Guarantees that there are no broken code paths No runtime dependencies: mock objects
C E
A D
Tests combination of classes and runtime environment Require runtime support Detect incompatibilities between components Residual code logic tests
A D
C E
4
QA System Test
QA Function Test
Function Integration
Dev
Runtime dependencies
Development
Code logic
5
Complex
Create user accounts, upgrade OS, install fixpacks, etc
Slow
Minutes to recycle servers between tests
Unnecessary
During development, you want to test the code, purge logic errors before starting test integration 6
Environment simulators
Respond like the real environment Pros: Simulate complex behavior without environment dependencies Cons: Few simulators available, maintaining a simulator rivals the complexity of maintaining a real application
Same interfaces as the real environment Developer must preset the mock objects with the expected responses Pros: Mock objects can be generated quickly for any environment Cons: Presetting mock objects for unit tests may require lots of code
9
JMockit
Instructs the JVM to replace calls to any method with code under your control One-two punch with JMock against bunkered initialization within the production code, such as calls to constructors or static methods Use JMockit to intercept calls to static methods deep into the production code and return JMock pre-programmed objects. http://jmockit.dev.java.net
JMock
Pre-program objects to expect calls and return values of your choice Instruct the production code to talk to mocked object instead of actual system dependency Mocks interfaces and classes. http://www.jmock.org 10
Mock libraries
@Test public void testTransferFunds() throws Exception { org.jmock.Mockery context = new Mockery(); context.setImposteriser(ClassImposteriser.INSTANCE); final float testAmount = 300; final BankAccount sourceAccount = context.mock(BankAccount.class, "source"); final BankAccount targetAccount = context.mock(BankAccount.class, "target"); org.jmock.Expectations expect = new Expectations() { { one(sourceAccount).withdraw(testAmount); one(targetAccount).deposit(testAmount); } }; context.checking(expect); Transaction tx = new Transaction(); tx.transferFunds(testAmount, sourceAccount, targetAccount); context.assertIsSatisfied(); }
@Mocked private final BankSOAUtil unused = null; @Test public void testBankAccountSOA() throws Exception { String inputDir = "abc"; final String inputFile = "AndrewBankAccountData.xml"; new mockit.Expectations() { { BankSOAUtil.getInputStream((URL)withNotNull()); returns(new FileInputStream(inputFile)); } }; // Bank account constructor reads its configuration using // BankSOAUtil.getInputStream BankAccount bc = new BankAccount("userid", "password"); Assert.assertEquals("Andrew", bc.getCustomerName()); }
12
public void testBankAccountSOA() throws Exception {s Mockit.setUpMocks(MockBankSOAUtil.class); String inputFile = "AndrewBankAccountData.xml"; // Bank account constructor reads its configuration using // BankSOAUtil.getInputStream BankAccount bc = new BankAccount(...); Assert.assertEquals("Andrew", bc.getCustomerName()); Mockit.tearDownMocks(); } @MockClass(realClass = BankSOAUtil.class) public static class MockBankSOAUtil { private static String inputFile = null; @Mock(invocations = 1) public static InputStream getInputStream(URL inputUrl) throws IOException { return new FileInputStream(inputFile) ; } public static void setInputFile(String inputFile) { MockDogearUtil.inputFile = inputFile ; } }
Instructs the JVM to stop using JMockit mocks. Declares the production class to be mocked
JMockit
Can intercept any call to the JVM Added many improvements to expectation support during 2009 Supports bytecode level coverage metrics My new favorite
JMock
Used to have better control over behavior of mocked object My former favorite Needs production code to provide entry points to receive the mocked objects
14
Is it important that the mocked method be actually called with a specific parameter?
Both JMock and JMockit offer out-of-the-box matchers for concepts like 'same value', 'same class', 'inherited from a class', 'null', 'not null', and others
final ExternalClass ecMock = context.mock(ExternalClass.class); org.jmock.Expectations expect = new org.jmock.Expectations() { { one(ecMock).somethingWithAReturnValue("specific value"); will(returnValue("someReturnValue")); } };
final ExternalClass ecMock = context.mock(ExternalClass.class); org.jmock.Expectations expect = new org.jmock.Expectations() { { one(ecMock).somethingWithAReturnValue(with(any(String.class))); will(returnValue("someReturnValue")); } };
Use with(any(Class <T> type)) to instruct the JMock mock to ignore input parameters
new };
mockit.Expectations () {
ExternalClass EcMock; { ecMock.somethingWithAReturnValue("specific value"); returns("someReturnValue")); }
new };
Use withAny(Class <T> type)) to instruct the JMockit mock to ignore input parameters
DDTUnit
http://ddtunit.sourceforge.net/
Cobertura
http://cobertura.sourceforge.net/
Note: JMockit has its own code coverage support
19