Spring Boot控制器测试中MockMvc始终为null的问题求助
最关键的问题是测试类注解冲突,直接导致MockMvc无法被正确注入:
你同时用了@WebMvcTest和@SpringBootTest,这俩注解根本不能一起用:
@WebMvcTest是专门测控制器的切片测试,只会初始化Spring MVC相关的组件,而且已经自动配置了MockMvc。@SpringBootTest是启动整个Spring上下文的全量测试,一般用来做集成测试。
俩注解混着用会让Spring容器初始化乱套,@Autowired的MockMvc自然就成null了。
另外,@ExtendWith(MockitoExtension.class)在Spring Boot 2.2之后完全没必要加,@WebMvcTest已经自带Mockito支持;@AutoConfigureMockMvc也是多余的,@WebMvcTest已经帮你配好了MockMvc。
除此之外,代码里还有几个必须改的小问题:
1. 测试方法里的变量错误
你把ResultActions类型的变量写成了Response,而且变量名大小写还不一致,直接会编译报错:
// 错误写法 Response ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/books/list")); response.andExpect(MockMvcResultMatchers.status().isOk()); // 修正后 ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/books/list")); response.andExpect(MockMvcResultMatchers.status().isOk());
2. 控制器方法的访问修饰符错了
BookController里的listBooks方法是private的,Spring MVC根本没法识别这个接口,必须改成public:
// 错误写法 private ResponseEntity<Object> listBooks(){...} // 修正后 public ResponseEntity<Object> listBooks(){...}
3. POM依赖版本混乱
你的pom里parent是Spring Boot 2.1.0,但spring-boot-starter-web却用了3.0.0,这俩版本差了好几个大版本,3.0.0要求Java 17,但你配的是Java 11,肯定会出各种依赖冲突。而且junit-jupiter-engine和mockito-junit-jupiter完全没必要单独加,spring-boot-starter-test已经包含了这些依赖,手动加版本反而会搞出冲突。
给你改好的pom,用支持Java 11的Spring Boot 2.7.x稳定版本:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.15</version> <relativePath/> </parent> <groupId>com.tdd</groupId> <artifactId>exemplo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>TDD Exemplo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
最后给你修正后的测试类,别忘了用@MockBean模拟BookService,因为@WebMvcTest不会初始化业务层的Bean,不模拟的话控制器构造注入会失败:
@WebMvcTest(BookController.class) public class TestControlBook { @Autowired private MockMvc mockMvc; @MockBean private BookService bookService; @Test public void testListBooks() throws Exception{ // 模拟业务层返回空列表,避免空指针 when(bookService.listBooks()).thenReturn(Collections.emptyList()); ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/books/list")); response.andExpect(MockMvcResultMatchers.status().isOk()); } }
内容的提问来源于stack exchange,提问作者Allan

