简体   繁体   English

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

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

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. Spring Boot 1.4具有许多优良功能,包括@DataJpaTest注释,可自动唤醒类路径嵌入式数据库以进行测试。 As far I know, it won't work in conjuction with TestRestTemplate in bounds of the same class. 据我所知,它不会与同一类的边界中的TestRestTemplate结合使用。

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? ...如文档所示,很容易模拟personService但是如何处理在这种情况下不可模拟的errors对象? 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. 所以,我无法使用切片方法和积分方法测试上面的代码,因为@DataJpaTest不应该与控制器一起使用。

Is there a way to test the controller with such architecture using Spring Boot 1.4 testing features? 有没有办法使用Spring Boot 1.4测试功能来测试具有这种架构的控制器?

Your understanding of the @DataJpaTest is a little off. 您对@DataJpaTest理解有点@DataJpaTest From the documentation "Can be used when a test focuses only on JPA components". 从文档“当测试仅关注JPA组件时可以使用”。 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. 如果您想要测试控制器层,则不希望使用此批注,因为WebMvc组件都不会加载到应用程序上下文中。 You instead want to use the @WebMvcTest and have it use the @Controller that you are testing. 你想要使用@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());
    }
}

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. 示例PersonRequest因为我不知道那里还有什么。 Note the one validation on the name as being @NotNull as I wanted a way to show how to use the Errors object. 注意名称上的一个验证是@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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM