使用MockMvc测试依赖Spring Validator的SpringBoot控制器遇注入异常
解决Spring Boot RestController自定义Validator单元测试找不到的问题
这个问题我之前帮同事排查过,本质就是测试上下文里没正确加载你的自定义JobValidator,或者构造控制器实例时没给全依赖。毕竟你用的是构造注入,控制器的所有构造参数都得有合法的实例才能初始化成功。下面给你两种靠谱的解决方案,都是用MockMvc来完成测试的:
方案一:用@WebMvcTest结合@MockBean(官方推荐的Web层测试方式)
这种方式会加载最小化的Web测试上下文,既能利用Spring的自动配置,又能Mock掉不需要的业务依赖,同时把自定义Validator纳入上下文:
测试类的核心配置:
- 用
@WebMvcTest(JobController.class)指定要测试的控制器,Spring会自动加载Web层相关的Bean - 用
@Import(JobValidator.class)把你的自定义Validator导入测试上下文,这样Spring就能找到它并注入到控制器里 - 用
@MockBean来MockJobService,避免测试时依赖真实的业务逻辑实现
- 用
完整示例代码:
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.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(JobController.class) @Import(JobValidator.class) // 关键:把自定义Validator加入测试上下文 public class JobControllerTest { @Autowired private MockMvc mockMvc; @MockBean // Mock业务层服务,隔离真实逻辑 private JobService jobService; @Test void testInvalidJobRequest() throws Exception { // 构造不符合校验规则的JSON请求 String invalidJobJson = "{\"title\":\"\",\"salary\":-100}"; mockMvc.perform(post("/jobs") .contentType(MediaType.APPLICATION_JSON) .content(invalidJobJson)) .andExpect(status().isBadRequest()); // 校验失败,预期返回400 } @Test void testValidJobRequest() throws Exception { String validJobJson = "{\"title\":\"Senior Java Developer\",\"salary\":18000}"; mockMvc.perform(post("/jobs") .contentType(MediaType.APPLICATION_JSON) .content(validJobJson)) .andExpect(status().isOk()); // 校验通过,预期返回200 } }
这里要注意:如果你的JobValidator本身是用@Component标记的Spring Bean,那其实不用@Import也能被上下文加载,但如果它只是普通类(没加Spring注解),@Import就必须加,不然Spring找不到这个类的实例。
方案二:手动构造控制器实例(灵活度更高的测试方式)
如果你不想依赖Spring的测试上下文,想完全手动控制所有依赖的实例化,那可以用这种方式:
核心步骤:
- 用Mockito来Mock
JobService - 手动创建
JobValidator的实例(或者也可以Mock它,如果你不想测试校验逻辑,只测控制器的其他逻辑) - 手动构造
JobController实例,把Validator和Mock的Service都传进去 - 用
MockMvcBuilders.standaloneSetup构建MockMvc,并显式设置Validator
- 用Mockito来Mock
完整示例代码:
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.http.MediaType; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class JobControllerManualTest { private MockMvc mockMvc; @Mock private JobService jobService; // Mock业务层依赖 private JobValidator jobValidator; private JobController jobController; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); // 初始化Mockito的Mock实例 jobValidator = new JobValidator(); // 创建真实的Validator实例 jobController = new JobController(jobValidator, jobService); // 手动构造控制器 // 构建MockMvc并绑定控制器,显式设置Validator确保校验生效 mockMvc = MockMvcBuilders.standaloneSetup(jobController) .setValidator(jobValidator) .build(); } @Test void testInvalidJobRequest() throws Exception { String invalidJobJson = "{\"title\":\"\",\"salary\":-5000}"; mockMvc.perform(post("/jobs") .contentType(MediaType.APPLICATION_JSON) .content(invalidJobJson)) .andExpect(status().isBadRequest()); } }
这种方式完全脱离Spring上下文,适合需要高度自定义依赖的测试场景,比如你想给Validator传一些特殊的配置参数时,手动实例化会更方便。
额外排查要点
如果还是遇到Validator找不到的错误,可以检查这几点:
- 确认
JobValidator的类名、构造方法有没有拼写错误,和控制器构造注入的参数类型是否完全匹配 - 如果Validator依赖其他Spring Bean,那在方案一中需要把这些依赖也加入测试上下文(用
@MockBean或者@Import),方案二中要手动注入这些依赖 - 如果你用的是Spring Boot 2.x以上版本,确保
spring-boot-starter-test依赖已经正确引入,里面包含了MockMvc和Mockito的相关包
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

