【问题标题】:Spring Boot 1.4 - how to test a controller with the validationSpring Boot 1.4 - 如何通过验证测试控制器
【发布时间】:2016-08-15 18:49:31
【问题描述】:

Spring Boot 1.4 有许多优秀的特性,包括 @DataJpaTest 注释,它可以自动唤醒类路径嵌入式数据库以进行测试。据我所知,它不能与同一类范围内的 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 对象呢?据我所知,没有办法模拟它,因为它不是类字段或方法的返回值。

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

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

【问题讨论】:

  • 您可以模拟 URL 调用,而不是模拟控制器方法。这将负责错误验证。查看此帖子stackoverflow.com/a/12308698/5343269
  • @11thdimension 值得指出的是,在 Spring Boot 1.4 中,注释略有变化,您不需要创建 MockMvc,而是可以自动插入它。查看spring.io/blog/2016/04/15/… 了解详细信息。
  • @wilddev 如果您可以包含您尝试使用的测试类,那么我们可以就如何使其按您期望的方式工作提供一些建议。
  • @ShawnClark,是的,完成了,看看

标签: java spring-mvc testing spring-boot integration-testing


【解决方案1】:

您对@DataJpaTest 的理解有些偏差。来自文档“可以在测试仅关注 JPA 组件时使用”。如果你想测试你的控制器层,你不想使用这个注解,因为没有一个 WebMvc 组件被加载到应用程序上下文中。相反,您想使用 @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;
    }
}

【讨论】:

    猜你喜欢
    • 2018-04-27
    • 2016-10-29
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2019-07-22
    • 2017-04-23
    • 2017-06-08
    • 2017-10-03
    相关资源
    最近更新 更多