Spring Boot JUnit5单元测试getSerieById失败,verify校验不通过求助
使用Spring Boot与JUnit5编写的getSerieByIdSimplifiedTest单元测试无法正常运行:控制器和SerieService实现逻辑无问题,但保留verify校验时就会报错,移除verify则测试正常。已通过@MockBean注入SerieService,却仍出现如下错误:
Wanted but not invoked: vidaria.services.SerieService#0 bean.getSeriesById(1L );
测试代码如下:
package com.garmanaz.vidaria.controllers; import com.fasterxml.jackson.databind.ObjectMapper; import com.garmanaz.vidaria.entities.Serie; import com.garmanaz.vidaria.services.SerieService; import com.garmanaz.vidaria.utils.JWT.JwtRequestFilter; import com.garmanaz.vidaria.utils.JWT.JwtTokenUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import static org.junit.jupiter.api.Assertions.assertSame; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(controllers = SerieController.class) @WithMockUser(username = "john", roles = {"USER"}) public class SerieControllerTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private SerieService serieService; @MockBean private JwtTokenUtil jwtTokenUtil; @MockBean private JwtRequestFilter jwtRequestFilter; @Autowired private SerieController serieController; @MockBean private RestTemplateBuilder restTemplateBuilder; @BeforeEach void setUp() { ReflectionTestUtils.setField(serieController, "serieService", serieService); } @Test void verifyMockInjection() { SerieService injectedService = (SerieService) ReflectionTestUtils.getField(serieController, "serieService"); System.out.println("Injected SerieService: " + injectedService); assertSame(injectedService, serieService, "The injected service is not the mock"); } @Test void getSerieByIdSimplifiedTest() throws Exception { long serieId = 1L; Serie mockSerie = Serie.builder() .id(serieId) .title("Test Serie") .build(); when(serieService.getSeriesById(serieId)).thenReturn(mockSerie); mockMvc.perform(get("/series/{serieId}", serieId) .contentType("application/json")) .andDo(print()) .andExpect(status().isOk()); verify(serieService, times(1)).getSeriesById(1L); } }
1. 移除多余的手动注入代码
@WebMvcTest注解会自动将@MockBean声明的SerieService注入到SerieController中,不需要在setUp方法里用ReflectionTestUtils手动设置。这种重复操作可能导致注入的Mock实例不一致,使得实际调用的Service和你verify的不是同一个对象。
解决方法:删除setUp方法以及verifyMockInjection测试方法,让Spring自动完成MockBean的注入。
2. 明确参数匹配规则
Mockito的参数匹配默认是严格匹配的,建议在when和verify中使用eq()匹配器明确指定参数,避免因基本类型/包装类型差异、重载方法等导致的匹配失败:
when(serieService.getSeriesById(eq(serieId))).thenReturn(mockSerie); // ... verify(serieService, times(1)).getSeriesById(eq(serieId));
3. 确认请求路径与控制器映射完全匹配
检查SerieController类或方法上的@RequestMapping注解,确认测试中的请求路径(/series/{serieId})与控制器的映射完全一致。如果控制器类上有前缀(比如@RequestMapping("/api")),测试请求需要加上对应前缀,例如/api/series/{serieId}。
可以通过andDo(print())打印的日志,查看请求是否真的路由到了目标控制器方法。
4. 校验方法名拼写一致性
确认SerieService中的方法名确实是getSeriesById,且控制器中调用的是该方法,避免因拼写错误导致Mock未被触发。
修正后的测试代码示例
package com.garmanaz.vidaria.controllers; import com.fasterxml.jackson.databind.ObjectMapper; import com.garmanaz.vidaria.entities.Serie; import com.garmanaz.vidaria.services.SerieService; import com.garmanaz.vidaria.utils.JWT.JwtRequestFilter; import com.garmanaz.vidaria.utils.JWT.JwtTokenUtil; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(controllers = SerieController.class) @WithMockUser(username = "john", roles = {"USER"}) public class SerieControllerTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private SerieService serieService; @MockBean private JwtTokenUtil jwtTokenUtil; @MockBean private JwtRequestFilter jwtRequestFilter; @MockBean private RestTemplateBuilder restTemplateBuilder; @Test void getSerieByIdSimplifiedTest() throws Exception { long serieId = 1L; Serie mockSerie = Serie.builder() .id(serieId) .title("Test Serie") .build(); when(serieService.getSeriesById(eq(serieId))).thenReturn(mockSerie); mockMvc.perform(get("/series/{serieId}", serieId) .contentType("application/json")) .andDo(print()) .andExpect(status().isOk()); verify(serieService, times(1)).getSeriesById(eq(serieId)); } }
内容的提问来源于stack exchange,提问作者Nazareno Garma

