英文:
MockMvc returned HttpMessageNotWritableException with application/json
问题
以下是您提供的代码的翻译部分:
我有一个使用 Spring 2.3.4.RELEASE 的 REST 终端点。
当我运行使用 MockMvc 进行控制器测试时,我收到以下错误:
> w.s.m.s.DefaultHandlerExceptionResolver : 已解析
> [org.springframework.http.converter.HttpMessageNotWritableException:
> 没有适用于 [class com.example.myexample.model.User] 和预设 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("获取 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/> <!-- 从仓库查找父级 -->
</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>
<!-- 其他依赖项 -->
<!-- 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>
<!-- 测试依赖项 -->
<dependency>
<!-- 其他依赖项 -->
</dependency>
</dependencies>
<!-- 其他设置 -->
</project>
如果我运行应用并通过 Postman 进行测试,端点可以正常工作。
有任何想法为什么我在我的 REST 控制器测试中会收到这个错误消息吗?
请注意,我只返回了您提供的代码翻译部分,未包含任何额外内容。如果您有更多问题或需要进一步的帮助,请随时提问。
英文:
I have a rest endpoint with spring 2.3.4.RELEASE
When I run test for controller with MockMvc I received
> 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> {
}
I've read other article that recommended to added jackson dependency, but this doesn't work for me.
<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>
This is my 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>
</project>
If I run the application and test it through postman, the endpoint works fine.
Any ideas why I get this error message on my rest controller test?
答案1
得分: 7
问题出在你的 @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 {
}
这应该可以解决你的问题。
英文:
The problem lies in your @SpringBootTest
, when you added classes = UserController.class
, it tells spring to create a context with UserController.class
. So it create spring context only with UserController. As a result only your controller bean is created and other beans needed for Http Message Converting is not created. So one solution is removing classes = UserController.class
from @SpringBootTest
.
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
Now you should have no problem running the tests.
But this comes with another problem, now this will load the full spring context which is not needed for only Controller Layer Testing. It will also increase the time of your unit tests.
To solve this problem , spring has another annotation @WebMvcTest . If you annotate yous tests with @WebMvcTest, Spring Boot instantiates only the web layer rather than the whole context. You can also choose to instantiate only one controller. For example,
@ExtendWith(SpringExtension.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
}
This should solve your problem.
答案2
得分: 1
显然我将UserControllerTest放在了错误的包名中。
将UserControllerTest与UserController放在相同的包名下后,它现在可以工作了。
英文:
Apparently I put the UserControllerTest in a wrong package name.
It's working now after I put the UserControllerTest in the same package name as the UserController.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论