MockMvc 返回 HttpMessageNotWritableException application/json
MockMvc returned HttpMessageNotWritableException with application/json
我有一个休息端点 spring 2.3.4.RELEASE
当我 运行 使用 MockMvc 测试控制器时,我收到
w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.http.converter.HttpMessageNotWritableException:
No converter for [class com.example.myexample.model.User] with preset
Content-Type 'application/json']
@SpringBootTest(classes = UserController.class)
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserRepository userRepository;
private static final String GET_USERBYID_URL = "/users/{userId}";
@Test
public void shouldGetUserWhenValid() throws Exception {
Address address = new Address();
address.setStreet("1 Abc St.");
address.setCity("Paris");
User userMock = new User();
userMock.setFirstname("Lary");
userMock.setLastname("Pat");
userMock.setAddress(address);
when(userRepository.findById(1)).thenReturn(Optional.of(userMock));
mockMvc.perform(get(GET_USERBYID_URL, "1").accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
}
@RestController
@RequestMapping(path = "/users")
@Slf4j
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping(value = "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(@PathVariable("userId") Integer userId) {
log.info("Getting user with id={}", userId);
final Optional<User> userOptional = userRepository.findById(userId)
return userOptional.map(value -> ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(value))
.orElseGet(() -> ResponseEntity.noContent()
.build()).ok()
.body(userOptional.get());
}
}
@Data
public class User {
private String firstname;
private String lastname;
private Address address;
}
@Data
public class Address {
private String street;
private String city;
}
@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
我读过其他建议添加 jackson 依赖项的文章,但这对我不起作用。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
这是我的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 http://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.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>myexample</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<lombok.version>1.18.12</lombok.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-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果我 运行 应用程序并通过邮递员对其进行测试,端点工作正常。
知道为什么我在休息控制器测试中收到此错误消息吗?
问题出在你的 @SpringBootTest
上,当你添加 classes = UserController.class
时,它告诉 spring 使用 UserController.class
创建上下文。因此它仅使用 UserController 创建 spring 上下文。结果,只创建了您的控制器 bean,而没有创建 Http 消息转换所需的其他 bean。因此,一种解决方案是从 @SpringBootTest
.
中删除 classes = UserController.class
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
现在你应该没有问题 运行 测试了。
但这带来了另一个问题,现在这将加载完整的 spring 上下文,这对于控制器层测试来说是不需要的。它还会增加单元测试的时间。
为了解决这个问题,spring 有另一个注解@WebMvcTest。如果您使用@WebMvcTest 注释您的测试,Spring Boot 仅实例化 web 层而不是整个上下文。您也可以选择只实例化一个控制器。例如,
@ExtendWith(SpringExtension.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
}
这应该可以解决您的问题。
显然我将 UserControllerTest 放在错误的包名中。
在我将 UserControllerTest 放入与 UserController 相同的包名称后,它现在可以工作了。
我有一个休息端点 spring 2.3.4.RELEASE 当我 运行 使用 MockMvc 测试控制器时,我收到
w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.example.myexample.model.User] with preset Content-Type 'application/json']
@SpringBootTest(classes = UserController.class)
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserRepository userRepository;
private static final String GET_USERBYID_URL = "/users/{userId}";
@Test
public void shouldGetUserWhenValid() throws Exception {
Address address = new Address();
address.setStreet("1 Abc St.");
address.setCity("Paris");
User userMock = new User();
userMock.setFirstname("Lary");
userMock.setLastname("Pat");
userMock.setAddress(address);
when(userRepository.findById(1)).thenReturn(Optional.of(userMock));
mockMvc.perform(get(GET_USERBYID_URL, "1").accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
}
@RestController
@RequestMapping(path = "/users")
@Slf4j
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping(value = "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(@PathVariable("userId") Integer userId) {
log.info("Getting user with id={}", userId);
final Optional<User> userOptional = userRepository.findById(userId)
return userOptional.map(value -> ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(value))
.orElseGet(() -> ResponseEntity.noContent()
.build()).ok()
.body(userOptional.get());
}
}
@Data
public class User {
private String firstname;
private String lastname;
private Address address;
}
@Data
public class Address {
private String street;
private String city;
}
@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
我读过其他建议添加 jackson 依赖项的文章,但这对我不起作用。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
这是我的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 http://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.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>myexample</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<lombok.version>1.18.12</lombok.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-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果我 运行 应用程序并通过邮递员对其进行测试,端点工作正常。 知道为什么我在休息控制器测试中收到此错误消息吗?
问题出在你的 @SpringBootTest
上,当你添加 classes = UserController.class
时,它告诉 spring 使用 UserController.class
创建上下文。因此它仅使用 UserController 创建 spring 上下文。结果,只创建了您的控制器 bean,而没有创建 Http 消息转换所需的其他 bean。因此,一种解决方案是从 @SpringBootTest
.
classes = UserController.class
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
现在你应该没有问题 运行 测试了。
但这带来了另一个问题,现在这将加载完整的 spring 上下文,这对于控制器层测试来说是不需要的。它还会增加单元测试的时间。
为了解决这个问题,spring 有另一个注解@WebMvcTest。如果您使用@WebMvcTest 注释您的测试,Spring Boot 仅实例化 web 层而不是整个上下文。您也可以选择只实例化一个控制器。例如,
@ExtendWith(SpringExtension.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
}
这应该可以解决您的问题。
显然我将 UserControllerTest 放在错误的包名中。 在我将 UserControllerTest 放入与 UserController 相同的包名称后,它现在可以工作了。