Spring Boot 1.4 - 如何使用验证来测试控制器

Spring Boot 1.4 - how to test a controller with the validation

Spring Boot 1.4 有许多优秀的特性,包括@DataJpaTest 注释,它可以自动唤醒 classpath 嵌入式数据库以进行测试。据我所知,它不会在相同 class.

的范围内与 TestRestTemplate 结合使用

以下测试无效:

@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");
    }

测试启动时会抛出异常:

Unsatisfied dependency expressed through field 'restTemplate': 
No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate]

然后猜测切换到推荐的基于模拟的切片方法,但它在那里不起作用,因为控制器看起来像这样:

@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);
}

...正如文档所建议的那样,很容易模拟 personService 但如何与在这种情况下不可模拟的 errors 对象一起使用?据我所知,没有办法模拟它,因为它不是 class 字段或方法的返回值。

因此,我无法使用切片方法或集成方法测试上面的代码,因为 @DataJpaTest 不应与控制器一起使用。

有没有办法使用 Spring Boot 1.4 测试功能来测试具有这种架构的控制器?

您对 @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 的理解并让它使用您正在测试的 @Controller

@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());
    }
}

不确定您是如何连接验证器和服务的,所以假设您是自动连接它们的。

@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);
    }
}

示例 PersonRequest 因为我不知道里面还有什么。请注意名称上的一个验证是 @NotNull,因为我想要一种方法来展示如何使用 Errors 对象。

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;
    }
}