Spring Boot @RestController测试报错:依赖注入失败及ApplicationContext加载异常
Spring Boot @RestController测试报错:No qualifying bean of type 'MemberService' available
问题描述
在Spring Boot中编写@RestController的测试代码时,出现以下错误:
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) --------------------------------------------------------------------------- Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberController' defined in file [C:\dev\react\Kculter\target\classes\com\prac\react\controller\MemberController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.prac.react.service.MemberService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} -------------------------------------------------------------------------------------------------- Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.prac.react.service.MemberService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
已为MemberService添加@Service注解、MemberController添加@RestController注解,但问题依旧。相关代码如下:
MemberControllerTest.java
package com.prac.react.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; import com.prac.react.model.dto.Member; import com.prac.react.service.MemberServiceTest; @WebMvcTest(MemberController.class) public class MemberControllerTest { @Autowired MockMvc mvc; // 用于测试虚拟HTTP请求的实例 @Autowired ObjectMapper obm; Logger logger = LoggerFactory.getLogger(MemberServiceTest.class); @Test @DisplayName("登录测试 1 ") // 模拟会员已存在场景的测试代码 void testSignInMember() throws Exception { // given Member mb = new Member(1, "hankgood95@gmail.com", "이욱재", true); String requestBody = obm.writeValueAsString(mb); mvc.perform(post("/member") .content(requestBody) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) // 状态码为200 .andExpect(content().string(".com")) // 返回内容包含.com .andDo(print()); // 打印请求相关信息 } }
MemberController.java
package com.prac.react.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.prac.react.model.dto.Member; import com.prac.react.service.MemberService; /* 该文件是处理会员注册、登录、会员信息修改等会员相关操作的控制器 */ @RestController public class MemberController { // 用于打印日志的实例 Logger logger = LoggerFactory.getLogger(MemberController.class); // 用于依赖注入的MemberService实例 MemberService ms; public MemberController(MemberService ms){ this.ms = ms; // 依赖注入 } @PostMapping("member") public Member SignInMember(@RequestBody Member member){ if(ms.checkMember(member.getEmail()) > 0){ // 会员已存在时进入 // 会员已存在,需携带当前Member信息跳转至主页 member.setCheckMember(true); return member; }else{// 首次注册时进入 // 非会员,需跳转至注册页面 member.setCheckMember(false); return member; } } }
MemberService.java
package com.prac.react.service; import org.springframework.stereotype.Service; import com.prac.react.model.dao.MemberDao; @Service public class MemberService { MemberDao md; // 通过构造函数注入MemberDao实例,注意:当存在多个依赖注入时必须添加@Autowired注解 public MemberService(MemberDao md){ this.md = md; } public int checkMember(String email){ return md.checkMember(email); } }
问题根源
@WebMvcTest注解的设计目标是仅加载Web层相关Bean(如@RestController、@Controller、@ControllerAdvice等),不会自动加载Service、Repository层的Bean。因此测试上下文启动时,Spring找不到MemberService的实例,导致控制器的依赖注入失败。
解决方案
方案一:使用@MockBean模拟MemberService(推荐单元测试使用)
因为控制器单元测试只需要验证请求处理逻辑,不需要真实的Service实现,用@MockBean创建一个模拟的MemberService实例注入到控制器即可:
修改后的MemberControllerTest.java:
package com.prac.react.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.mockito.Mockito.when; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; import com.prac.react.model.dto.Member; import com.prac.react.service.MemberService; @WebMvcTest(MemberController.class) public class MemberControllerTest { @Autowired MockMvc mvc; @Autowired ObjectMapper obm; @MockBean // 模拟MemberService实例,注入到控制器 MemberService memberService; Logger logger = LoggerFactory.getLogger(MemberControllerTest.class); // 修正Logger引用为当前测试类 @Test @DisplayName("登录测试 1 - 模拟会员已存在场景") void testSignInMember() throws Exception { // given Member mb = new Member(1, "hankgood95@gmail.com", "이욱재", true); // 模拟Service的checkMember方法返回1(表示会员存在) when(memberService.checkMember(mb.getEmail())).thenReturn(1); String requestBody = obm.writeValueAsString(mb); mvc.perform(post("/member") .content(requestBody) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string("true")) // 断言返回的Member中checkMember为true .andDo(print()); } }
说明:
@MockBean会让Spring自动创建MemberService的模拟实例,解决依赖缺失问题- 用
when(...).thenReturn(...)指定模拟方法的返回值,模拟业务场景 - 修正了原代码中Logger的错误引用(原代码用了
MemberServiceTest,改为当前测试类) - 调整了断言内容,原断言
.com不符合实际返回结果,改为断言checkMember为true
方案二:改用@SpringBootTest + @AutoConfigureMockMvc(集成测试使用)
如果需要使用真实的Service、Dao层实现,测试完整的业务流程,可以用@SpringBootTest加载整个应用上下文,配合@AutoConfigureMockMvc启用MockMvc:
修改测试类的注解:
@SpringBootTest @AutoConfigureMockMvc public class MemberControllerTest { // 其余代码可保持不变,或根据真实业务场景调整 }
说明:
@SpringBootTest会加载整个Spring应用上下文,所有Bean(包括Service、Dao)都会被初始化- 这种方式启动速度较慢,适合集成测试,而非单元测试
额外优化:简化构造函数注入
Spring 4.3+支持无@Autowired的构造函数注入,但为了代码可读性,建议使用Lombok的@RequiredArgsConstructor注解自动生成构造函数,简化代码:
修改MemberController.java:
@RestController @RequiredArgsConstructor // Lombok注解,自动生成含final字段的构造函数 public class MemberController { private final Logger logger = LoggerFactory.getLogger(MemberController.class); private final MemberService ms; // 必须声明为final @PostMapping("member") public Member SignInMember(@RequestBody Member member){ // 业务逻辑不变 } }
修改MemberService.java:
@Service @RequiredArgsConstructor public class MemberService { private final MemberDao md; public int checkMember(String email){ return md.checkMember(email); } }
内容的提问来源于stack exchange,提问作者codingRookie
相关产品推荐
相关产品推荐

