【问题标题】:Mockito - how to answer with custom objectMockito - 如何用自定义对象回答
【发布时间】:2016-09-18 10:54:31
【问题描述】:

尝试像这样测试构造:

@RequestMapping(value = "/test", method = POST)
public ResponseEntity test(@RequestBody TestRequest request, Errors errors) {

    testValidator.validate(request, errors); // Spring Validator interface impl


    if (errors.hasErrors())
        return new ResponseEntity(HttpStatus.BAD_REQUEST);

    return new ResponseEntity(HttpStatus.OK);
}

Errors 对象是由框架而不是我传递的,所以除了使用 Mockito 的 Answer 之类的东西之外,没有更好的方法来改变它的测试行为:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ExampleTest {

    @Autowired
    private TestRestTemplate testRestTemplate;

    @MockBean
    private TestValidator testValidator; 

    @Test
    public void test() {

        doAnswer(new Answer<Errors>() {

           @Override
           public Errors answer(InvocationOnMock invocation) throws Throwable {

             Errors errors = spy((Errors) invocation.getArguments()[1]);
             //errors.rejectValue("id", "id", "id rejected");

             doReturn(true).when(errors).hasErrors();

             return errors;
         }

      }).when(testValidator).validate(any(), any());

      ResponseEntity re = testRestTemplate
        .postForEntity("/test", new TestRequest(213L), String.class);

      assertEquals(HttpStatus.BAD_REQUEST, re.getStatusCode());
  }

...但问题是errors.hasErrors() 仍然返回false,尽管在doAnswer 测试块上并且断言由于HTTP 状态OK 而失败。我预计Errors 对象将在.validate() 调用后立即成为具有指定行为的间谍,但似乎我做错了什么。

那么,如何使用 Mockito 的 doAnswer 返回自定义对象?

【问题讨论】:

  • 按照你的方式,你如何将你的testValidator“注入”到被测试的实例中?
  • (我也建议你使用spring-mvc-test)
  • @RC.,使用新的 Spring 测试功能 @MockBean 注释,其行为与 Mockito 的 @Mock 完全相同
  • @RC.,由于 Spring 引入了另一种 slice 方法,该方法似乎已被弃用

标签: java spring testing spring-boot mockito


【解决方案1】:

不要在你的测试中使用postForEntity,因为这使它成为框架本身的集成测试,加上你正在测试的实际类。

只需制作您自己的 Errors 对象,它可能是也可能不是模拟对象。如果是 mock,那么你可以如下设置。

doReturn(true).when(mockErrors).hasErrors();

那么你的测试可以是这样的。

@Test
public void test() {
  ResponseEntity re = objectThatYoureTesting.test(mockTestRequest, mockErrors);
  assertEquals(HttpStatus.BAD_REQUEST, re.getStatusCode());
}

【讨论】:

    【解决方案2】:

    doReturn(true).when(errors).hasErrors() 仅在代码块使用间谍errors 实例时才有效。如果您将返回的 Error 引用分配给变量,模拟应该可以工作:

    errors = testValidator.validate(request, errors);
    

    您将添加此分配仅用于测试目的,但它也有利于代码的可读性;它强烈表明errors 实例在validate 方法内被修改。

    更新:您可以包装验证器以返回错误,而不是监视包装的验证器:

    public class ValidatorWrapper {
        public Errors validate(TestRequest request, Errors errors) {
            testValidator.validate(request, errors);
            return errors;
        }
    }
    

    并且测试代码将类似于:

    controller.setValidatorWrapper(spiedValidatorWrapper);
    doAnswer(new Answer<Errors>() {
    
           @Override
           public Errors answer(InvocationOnMock invocation) throws Throwable {
    
             Errors errors = spy((Errors) invocation.getArguments()[1]);
             //errors.rejectValue("id", "id", "id rejected");
    
             doReturn(true).when(errors).hasErrors();
    
             return errors;
         }
    
      }).when(spiedValidatorWrapper).validate(any(), any());
     //rest of the test
    

    这应该可以解决问题。

    【讨论】:

    • hm testValidator 是 Spring Validator 接口实现,所以没有选项使用返回值
    • 我假设有一个,因为您在答案块中返回了它。那么就没有办法窥探Error实例。
    猜你喜欢
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    相关资源
    最近更新 更多