Defining Units, Integration Testing Responsibilities, and the Evolution of Mock Frameworks in Java Backend Development
This article explains how to define a unit in testing, discusses who should write unit and integration tests, presents three generations of a custom Java mock framework with code examples, compares its pros and cons, and introduces EvoSuite for automatic test generation within Maven projects.
How to Define a Unit
In unit testing, a "unit" can be interpreted as a single method, a complete interface implementation, an entire functional module, or a set of coupled modules, depending on the perspective of the tester.
When designing unit test cases, method‑level tests focus on the underlying logic of a method, module‑level tests focus on business logic implementation, and tests that span multiple modules are generally called integration tests. Both unit and integration tests are essentially tests of methods or interfaces, differing only in the stage at which they are applied.
Who Should Write Integration Tests
In practice, developers often write "smoke"‑level integration tests before submitting code, while testers create comprehensive integration test cases after the whole feature is completed, based on business requirements and existing test designs.
Integration Test Case Structure
Business‑related integration tests are usually built with spring-test . A base test class is defined to initialise the class under test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:./spring/applicationContext.xml"})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class BaseSpringJunitTest {
@Autowired
protected BusinessRelatedServiceImpl businessRelatedService;
}A concrete test class extends this base class:
public class BusinessRelatedServiceImplDomainTest extends BaseSpringJunitTest {
@Test
public void testScenario1() {
new Thread(new DOSAutoTest("testScenario1")).start();
Thread.sleep(1000 * 60 * 1);
String requestJson = ""; // test input
RequestPojo request = (RequestPojo) JSONUtils.jsonToBean(requestJson, RequestPojo.class);
ResponsePojo response = businessRelatedService.businessRelatedMethod(ResponsePojo.class);
// assert business logic
}
}How to Solve Down‑stream System Dependencies
The businessRelatedMethod calls a downstream JSF (Jingdong Service Framework) order service to retrieve order details. In functional or integration testing, obtaining realistic downstream data is costly, so mocking the downstream service becomes essential.
Mock Framework – First Version
The first version overrides all downstream interface methods, deploys the implementation in a test environment, and distinguishes mock services via different JSF aliases. Mock data is stored in a database.
Base mock class example:
public class OrderverExportServiceImp extends OrderverExportServiceAdapter {
@Resource
private OrderverMapper orderverMapper;
@Override
public ResultPojo getOrderInfoById(long orderId) {
OrderverPojo orderverMock = orderverMapper.getOrderId(Long.toString(orderId));
ResultPojo result = new ResultPojo();
result.setFiled1(null);
result.setFiled1(0);
result.setFiled2(null);
result.setFiled3(null);
result.setResult(true);
// ... set other fields
result.setReturnObject(orderverMock);
return result;
}
}Advantages:
No code is required when designing integration test cases; only test data needs to be maintained.
The same framework can be reused for system and joint testing as long as downstream interfaces remain unchanged.
Disadvantages:
The mock service depends on a server and database; if either fails, the mock becomes unavailable.
When many downstream interfaces exist, each method must be overridden, increasing maintenance cost.
Interface changes require re‑overriding, repackaging, and redeployment.
Data‑structure changes in downstream interfaces demand database schema updates.
Mock Framework – Second Version
To eliminate server and database dependencies, the second version packages the mock as a JAR. Business data is stored in files, and the JSF consumer node is replaced with the mock implementation class.
@Service("orderverExportService")
public class OrderverExportServiceMock extends OrderverExportServiceAdapter {
@Override
public ResultPojo getOrderInfoById(long orderId) {
ResultPojo result = new ResultPojo();
result.setFiled1(null);
result.setFiled1(0);
result.setFiled2(null);
result.setFiled3(null);
result.setResult(true);
// ... load mock data from file
return result;
}
}Advantages:
Same as version one, but test data is loaded from local files, improving execution speed.
No need for server deployment or database.
Disadvantages:
Still requires overriding every downstream method when many interfaces exist.
Interface additions or signature changes require re‑overriding, repackaging, and publishing to Maven.
Frequent data‑structure changes increase maintenance effort.
Mock Framework – Third Version
The third version leverages JDK dynamic proxies, reflection, and advanced JSF features to eliminate the need for per‑interface coding. Testers only need to prepare mock data (stored as JSON) while the framework handles method interception.
Key class DOSAutoTest starts and publishes the JSF mock service; JSFMock uses dynamic proxies to return appropriate mock data based on the test scenario.
Automatic Unit Test Generation with EvoSuite
For non‑business modules, EvoSuite can automatically generate JUnit‑compatible test cases. Although manual review is still required, EvoSuite greatly improves efficiency while maintaining coverage.
Maven plugin configuration (excerpt):
<plugin>
<groupId>org.evosuite.plugins</groupId>
<artifactId>evosuite-maven-plugin</artifactId>
<version>${evosuiteVersion}</version>
<executions>
<execution>
<goals>
<goal>prepare</goal>
</goals>
<phase>process-test-classes</phase>
</execution>
</executions>
</plugin>Additional dependencies and Surefire plugin settings are required to run both manually written and EvoSuite‑generated tests.
Command to generate tests:
mvn -DmemoryInMB=2000 -Dcores=2 evosuite:generate evosuite:export testConclusion
Selecting an appropriate mock framework—whether a generic third‑party tool or a custom solution—can reduce test‑code writing time, improve execution efficiency, and lower maintenance cost. The ultimate goal of mocking is to enable rapid design of diverse test scenarios, enhance product quality, and free testers from repetitive manual testing.
Future articles will discuss how to evaluate test efficiency, ROI of unit testing, and related topics.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.