Mockito使用doReturn仍触发Stub方法问题及集成测试方案咨询
Hey there, let's break down why your stubbed method is firing early and how to fix this common testing pitfall!
Possible Reasons for Early Method Execution
1. Incorrect Stubbing Syntax (The #1 Culprit)
If you wrote your doReturn stub like this, you’re accidentally triggering the real method call while setting up the stub:
// ❌ Wrong: Evaluates the method call inside `when()` immediately Mockito.doReturn(updatedProject) .when(projectService.synchronizeWithDamAndSave(any(Project.class)));
The expression inside when() gets executed before Mockito can intercept it, which runs the real synchronizeWithDamAndSave logic before your API request even starts.
2. Using a Real Instance Instead of a Spy/Mock
If your ProjectService is a raw, real Spring Bean (not wrapped with @SpyBean or @MockBean), Mockito can’t intercept its methods. Your doReturn setup will be ignored, and the real method will run whenever it’s called.
3. Early Triggering in Initialization Logic
Check your @BeforeEach/@BeforeAll methods or test class constructor—you might be accidentally calling code that invokes synchronizeWithDamAndSave before your stub is fully set up.
Fixes to Resolve the Issue
1. Correct the Stubbing Syntax
Rewrite your doReturn stub to avoid executing the real method during setup. The key is to separate the mock/spy reference from the method call:
// ✅ Correct: No real method call happens here Mockito.doReturn(mockUpdatedProject) .when(projectService) .synchronizeWithDamAndSave(any(Project.class));
This tells Mockito to intercept calls to synchronizeWithDamAndSave on projectService without running the real method first.
2. Use @SpyBean or @MockBean for Spring Integration Tests
In Spring Boot integration tests:
- Use
@SpyBeanif you want to keep most of the realProjectServicelogic but stub just this one method. - Use
@MockBeanif you want to replace the entireProjectServicewith a mock (good if you don’t need other methods to run real logic).
Example test setup:
@SpringBootTest @AutoConfigureMockMvc public class ProjectControllerUpdateTest { @Autowired private MockMvc mockMvc; @SpyBean // Wraps the real bean to allow selective stubbing private ProjectService projectService; @Test void testPutUpdateProject() throws Exception { // 1. Set up the stub FIRST Project stubbedResult = new Project(); stubbedResult.setId(1L); stubbedResult.setName("Updated Test Project"); doReturn(stubbedResult) .when(projectService) .synchronizeWithDamAndSave(any(Project.class)); // 2. Send the PUT request mockMvc.perform(put("/projects/1") .contentType(MediaType.APPLICATION_JSON) .content("{\"name\":\"Updated Test Project\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Updated Test Project")); // Optional: Verify the method was called as expected verify(projectService, times(1)) .synchronizeWithDamAndSave(any(Project.class)); } }
3. Adjust Initialization Order
Make sure all stubbing logic runs before any code that could trigger synchronizeWithDamAndSave. Move stub setup to the start of your test method, and double-check that @BeforeEach methods aren’t invoking related service logic prematurely.
4. Use Precise Argument Matchers
If your stub isn’t matching the actual method call (e.g., because you’re using a specific object instead of a matcher), Mockito will fall back to the real method. Use matchers like any(), eq(), or argThat() to ensure your stub catches the right calls:
// Stub only calls with a project ID of 1 doReturn(stubbedResult) .when(projectService) .synchronizeWithDamAndSave(argThat(p -> p.getId().equals(1L)));
内容的提问来源于stack exchange,提问作者Oncioiu Costin

