Mock objects isolate unit tests from external dependencies, ensuring consistent execution regardless of network availability or database state. The goal is truly portable, self-contained validation logic.
After evaluating several tools, Mockito and MockServer were selected for Java projects. Other candidates were set aside for various reasons:
- Rap2 and Easy-mock rely on Node.js and lack request timeout/context configuraton.
- Wiremock functions similar but presents a English-only interface.
- Postman's mock mode generates random, unpredictable port and URL assignments.
- MockServer, built on Netty with a native Java core, offers high adaptability for Java teams.
Mockito
Mockito, bundled with spring-boot-starter-test, allows simulation of object behavior. It eliminates the need for real RPC calls, databases, or utilities.
- Mock returns a bare object; unconfigured method calls yield null or an exception.
- Spy wraps a real object, invoking the original method for any behavior not explicitly stubbed.
Spring Integration
Add the dependency:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
Initialize mocks and swap the Spring-managed bean:
@Mock
RemotePaymentClient paymentClient;
@Autowired
OrderPaymentFacade orderPaymentFacade;
@Before
public void prepareMocks() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(
AopTargetUtils.getTarget(orderPaymentFacade),
"paymentClient",
paymentClient
);
PaymentResult mockResult = new PaymentResult();
when(paymentClient.queryByCardId("101010")).thenReturn(mockResult);
}
For Spring Boot 2.0+, simply annotate the field with @MockBean; the framework handles replacement automatically.
MockServer
MockServer simulates full HTTP environments, helping catch protocol-level issues. It can run embedded in test lifecycles or as a standalone service.
Setup
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.11.1</version>
</dependency>
Start the server and define dynamic responses:
private final int mockPort = 20000;
private ClientAndServer mockServer;
@Before
public void startMockServer() {
mockServer = startClientAndServer(mockPort);
mockServer
.when(
request()
.withMethod("POST")
.withPath("/gateway/payment/charge")
.withContentType(MediaType.APPLICATION_JSON)
)
.respond(new FlexibleCallback());
}
public static class FlexibleCallback implements ExpectationResponseCallback {
private final Gson gson = new Gson();
@Override
public HttpResponse handle(HttpRequest req) {
if (!req.getMethod().getValue().equals("POST")) {
return notFoundResponse();
}
if (!verifySignature(req)) {
return jsonError("SIGNATURE_VERIFY_FAIL");
}
return buildResponse(req);
}
private HttpResponse buildResponse(HttpRequest req) {
JSONObject body = JSON.parseObject(new String(req.getBodyAsRawBytes()));
Assert.assertNotNull(body.getString("merchant_id"));
String merchantId = body.getString("merchant_id");
String respBody = "{\"success\": true,\"code\": \"0000\",\"data\": {\"user\": \"" + merchantId + "\"}}";
// Simulate async callback after delay
new Thread(() -> {
LockSupport.parkNanos(2_000_000_000L);
String notifyUrl = body.getString("notify_url");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
JSONObject payload = new JSONObject();
payload.put("status", "finished");
new RestTemplate().postForObject(notifyUrl,
new HttpEntity<>(payload.toJSONString(), headers), String.class);
}).start();
return response().withStatusCode(200).withBody(respBody);
}
private boolean verifySignature(HttpRequest req) {
String sig = req.getFirstHeader("X-Signature");
String raw = new String(req.getBodyAsRawBytes(), StandardCharsets.UTF_8);
String md5 = DigestUtils.md5Hex(raw.getBytes(StandardCharsets.UTF_8));
return RSAUtils.doCheck(md5, sig, privateKey, StandardCharsets.UTF_8.displayName());
}
private HttpResponse jsonError(String msg) {
return response().withStatusCode(200).withBody(gson.toJson(Map.of("msg", msg)));
}
}
Cobertura Coverage Reports
Cobertura integrates with Maven to produce branch and line coverage metrics.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.7</version>
</plugin>
Run with:
mvn clean cobertura:cobertura -f pom.xml
Open target/site/index.html to inspect the coverage dashboard.