测试用例运行时出现Bean序列化异常,浏览器访问接口正常
问题:Spring Boot WebMvc测试接口失败,但浏览器访问正常
问题描述
浏览器访问/getEmployee接口功能正常,但运行WebMvc测试用例时抛出Jackson序列化异常,测试失败。
相关代码
Controller类
@RestController public class EmployeeController { @Autowired private EmployeeDTO employeeDTO; @GetMapping("/getEmployee") public EmployeeDTO getEmployee(){ employeeDTO.setEmpId(1); employeeDTO.setEmpName("Bob"); return employeeDTO; } }
EmployeeDTO类
@Getter @Setter @NoArgsConstructor @Component public class EmployeeDTO { private int empId; private String empName; }
EmployeeControllerTest类
@WebMvcTest public class EmployeeControllerTest { @Autowired private MockMvc mockmvc; @MockBean private EmployeeDTO employeeDTO; @Test public void testGetEmployee() throws Exception { RequestBuilder request = MockMvcRequestBuilders.get("/getEmployee"); mockmvc.perform(request) .andExpect(status().isOk()); } }
pom.xml
<?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.8</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.spring</groupId> <artifactId>tennis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>tennis</name> <description>Demo project</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-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
异常信息
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.spring.tennis.model.EmployeeDTO$MockitoMock$MCOb7jmM["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["serializationSupport"])
原因分析
- Mock对象序列化问题:测试时用
@MockBean生成的EmployeeDTO是Mockito代理对象,包含Mockito内部的拦截器、序列化支持类等属性,Jackson无法序列化这些未暴露的内部类,导致异常。 - DTO设计不合理:
EmployeeDTO被标记为@Component,成为Spring单例Bean。正常运行时Spring注入真实实例,Jackson可以序列化其属性;但测试时Mock替换了真实实例,触发序列化问题。同时,DTO作为数据传输对象,不应该被Spring管理为单例,应该每次请求创建新实例。
解决方案
方案一:修正DTO与Controller的设计(推荐)
这是从根源解决问题的方式:
- 移除DTO的
@Component注解:
@Getter @Setter @NoArgsConstructor public class EmployeeDTO { private int empId; private String empName; }
- 修改Controller,不再注入DTO,而是方法内创建新实例:
@RestController public class EmployeeController { @GetMapping("/getEmployee") public EmployeeDTO getEmployee(){ EmployeeDTO employeeDTO = new EmployeeDTO(); employeeDTO.setEmpId(1); employeeDTO.setEmpName("Bob"); return employeeDTO; } }
- 调整测试用例,无需Mock DTO:
@WebMvcTest public class EmployeeControllerTest { @Autowired private MockMvc mockmvc; @Test public void testGetEmployee() throws Exception { RequestBuilder request = MockMvcRequestBuilders.get("/getEmployee"); mockmvc.perform(request) .andExpect(status().isOk()) .andExpect(jsonPath("$.empId").value(1)) .andExpect(jsonPath("$.empName").value("Bob")); } }
方案二:临时适配测试用例(不推荐,仅用于无法修改原代码的场景)
如果暂时不能修改Controller和DTO的设计,可通过指定Mock对象的行为让Jackson正常序列化:
@WebMvcTest public class EmployeeControllerTest { @Autowired private MockMvc mockmvc; @MockBean private EmployeeDTO employeeDTO; @Test public void testGetEmployee() throws Exception { // 指定Mock对象的getter返回值,确保Jackson能序列化所需属性 given(employeeDTO.getEmpId()).willReturn(1); given(employeeDTO.getEmpName()).willReturn("Bob"); RequestBuilder request = MockMvcRequestBuilders.get("/getEmployee"); mockmvc.perform(request) .andExpect(status().isOk()) .andExpect(jsonPath("$.empId").value(1)) .andExpect(jsonPath("$.empName").value("Bob")); } }
或者禁用Jackson的FAIL_ON_EMPTY_BEANS特性(治标不治本,可能隐藏其他问题):
@WebMvcTest public class EmployeeControllerTest { @Autowired private MockMvc mockmvc; @MockBean private EmployeeDTO employeeDTO; @Bean public ObjectMapper objectMapper() { return new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); } @Test public void testGetEmployee() throws Exception { RequestBuilder request = MockMvcRequestBuilders.get("/getEmployee"); mockmvc.perform(request) .andExpect(status().isOk()); } }
内容的提问来源于stack exchange,提问作者ts009
相关产品推荐
相关产品推荐

