Reputation: 558
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>
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?
Upvotes: 0
Views: 2809
Reputation: 558
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.
Upvotes: 1
Reputation: 2744
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.
Upvotes: 7