Guidelines for Writing Unit Tests with Mockito and Test Case Design in SpringBoot
The article outlines how to design robust SpringBoot unit tests by first mastering business flows and external dependencies, preparing isolated test data, using Mockito’s @Mock/@MockBean and @Spy/@SpyBean correctly—including proper stubbing techniques—and applying best‑practice rules and the ACTS 2.0 framework for data‑driven, coverage‑verified testing.
Effective code design should follow the principle of easy modification, which requires frequent refactoring rather than large‑scale rewrites after code becomes bloated. To ensure that frequent changes do not break existing functionality, a comprehensive suite of test cases must be written to cover all execution paths.
The article emphasizes three preparatory steps before writing unit tests:
Fully understand the business flow, including normal and exceptional scenarios.
Identify all external interactions and data mutation points such as RPC calls, MQ messages, and database operations.
Prepare test data that does not affect real production data and can be easily cleaned up.
When writing tests in a SpringBoot environment, Mockito annotations are recommended. The following annotations are available:
@Mock and @MockBean – create mock objects; @MockBean registers the mock as a Spring bean.
@Spy and @SpyBean – create spy objects that call real methods unless stubbed; @SpyBean registers the spy as a Spring bean.
Key differences:
Mocks are not managed by Spring, while @MockBean and @SpyBean are Spring‑managed beans and can be autowired.
When using @Spy , real methods are invoked by default; to stub a method you must use Mockito.doReturn(...).when(...) instead of when(...).thenReturn(...) .
Example of stubbing a method return value:
/ 声明userService在传入任意参数调用get()方法时,返回user Mockito.when(userService.get(Mockito.any())).thenReturn(user); Mockito.doReturn(user).when(userService).get(Mockito.any()); // Spy必须用这种Example of stubbing an exception:
// 声明userService在传入任意参数调用get()方法时,抛出指定异常 Mockito.when(userService.get(Mockito.any())).thenThrow(new RuntimeException("mock error")); Mockito.doThrow(new RuntimeException("mock error")).when(userService).get(Mockito.any()); // Spy必须用这种Test cases should focus on internal logic without involving upstream or downstream services. For example, a transfer interface test should verify the synchronous acceptance process and treat asynchronous settlement as a separate test.
Additional best practices include:
Do not let tests modify external applications or real data.
Separate test data using unique identifiers (e.g., id, requestNo) and ensure cleanup scripts only affect test records.
Use coverage tools (e.g., Maven/Gradle coverage mode) to verify test completeness.
The article also introduces the ACTS (AntCoreTestSuite) 2.0 framework, an internal Ant Group solution for data‑driven automated testing of SOFA/SOFABoot services. ACTS provides a standardized lifecycle, visual test case editing, and fine‑grained result verification, dramatically reducing test code size.
Key components of ACTS include:
Test case directory structure (PrepareDBData for initial data, CheckDBData for verification).
Data files describe table columns, data types, primary keys, and a flag indicating insert, check, or cleanup actions.
During verification, ensure the cleanup flag (C) only deletes test data.
Running tests in coverage mode generates detailed coverage reports, helping developers identify untested code paths.
Ant R&D Efficiency
We are the Ant R&D Efficiency team, focused on fast development, experience-driven success, and practical technology.
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.