Reputation: 2367
Spring Boot 1.4 has a number of fine features including @DataJpaTest annotation that automatically wakes up the classpath embedded database for the test purposes. As far I know, it won't work in conjuction with TestRestTemplate in bounds of the same class.
The following test won't work:
@RunWith(SpringRunner.class)
@SpringBootTest
@DataJpaTest
public class PersonControllerTest {
private Logger log = Logger.getLogger(getClass());
private Category category;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private TestEntityManager entityManager;
@Before
public void init() {
log.info("Initializing...");
category = entityManager.persist(new Category("Staff"));
}
@Test
public void personAddTest() throws Exception {
log.info("PersonAdd test starting...");
PersonRequest request = new PersonRequest("Jimmy");
ResponseEntity<String> response = restTemplate.postForEntity("/Person/Add", request, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
log.info("PersonAdd test passed");
}
During startup of the test an exception will be thrown:
Unsatisfied dependency expressed through field 'restTemplate':
No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate]
Then guessing to switch to the recommended mock based slice approach but it won't work there because the controller looks like this:
@RequestMapping(value="/Person/Add", method=RequestMethod.POST)
public ResponseEntity personAdd(@Valid @RequestBody PersonRequest personRequest,
Errors errors)
personValidator.validate(personRequest, errors):
if (errors.hasErrors())
return new ResponseEntity(HttpStatus.BAD_REQUEST);
personService.add(personRequest);
return new ResponseEntity(HttpStatus.OK);
}
... it's easy to mock the personService
as the documentation suggests but how to be with the errors
object which is not mockable in this case? As far I know, there's no ways to mock it since it isn't class field or a returned value of a method.
So, I'm unable to test the code above using neither slice approach nor integration one since @DataJpaTest
should not be used with a controller.
Is there a way to test the controller with such architecture using Spring Boot 1.4 testing features?
Upvotes: 1
Views: 7293
Reputation: 3440
Your understanding of the @DataJpaTest
is a little off. From the documentation "Can be used when a test focuses only on JPA components". If you are wanting to test your controller layer you don't want to use this annotation as none of the WebMvc components get loaded into the application context. You instead want to use the @WebMvcTest
and have it use the @Controller
that you are testing.
@RunWith(SpringRunner.class)
@WebMvcTest(PersonController.class)
public class PersonControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
PersonValidator personValidator;
@MockBean
PersonService personService;
@Test
public void personAddTest() throws Exception {
String content = "{\"name\": \"Jimmy\"}";
mockMvc.perform(post("/Person/Add").contentType(MediaType.APPLICATION_JSON).characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON).content(content)).andExpect(status().isOk());
}
@Test
public void personAddInvalidTest() throws Exception {
String content = "{\"noname\": \"Jimmy\"}";
mockMvc.perform(post("/Person/Add").contentType(MediaType.APPLICATION_JSON).characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON).content(content)).andExpect(status().isBadRequest());
}
}
Not sure how you wired the validator and service so just assumed you autowired them.
@Controller
public class PersonController {
private PersonValidator personValidator;
private PersonService personService;
public PersonController(PersonValidator personValidator, PersonService personService) {
this.personValidator = personValidator;
this.personService = personService;
}
@RequestMapping(value = "/Person/Add", method = RequestMethod.POST)
public ResponseEntity<String> personAdd(@Valid @RequestBody PersonRequest personRequest, Errors errors) {
personValidator.validate(personRequest, errors);
if (errors.hasErrors()) {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
personService.add(personRequest);
return new ResponseEntity<String>(HttpStatus.OK);
}
}
Sample PersonRequest
as I didn't know what else was in there. Note the one validation on the name as being @NotNull
as I wanted a way to show how to use the Errors
object.
public class PersonRequest {
@NotNull
private String name;
public PersonRequest() {
}
public PersonRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Upvotes: 4