场景是我试图验证一个域对象,并将验证消息存储在另一个对象中。类是Validator
,方法看起来像:
public void validate (Domain domain, ValidationInfo info)
并且ValidationInfo
对象具有:
List<String> messages = new ArrayList<String> messages ()
Validator
类是从服务类调用的。无论域对象的验证失败,失败消息都将存储在ValidationInfo
类的列表中。
在其中一个测试用例的服务测试类中,我写了以下内容来测试验证失败:
@Test
public void testvalidation () throws Exception {
Domain domain = createDomainWithInvalidData();
service.create (domain);
verify(mockValidator).validate(domain, validationInfo);
assertFalse (validationInfo.getMessages().isEmpty());
现在,我想要的是从validationInfo
对象中获得失败消息,这样我就可以在此基础上编写一些断言。但它不起作用。
有人能解释一下吗。
据我所知,您正在对service
进行单元测试。
事实上,从validationInfo
获取消息是没有意义的,因为它被传递到Mockito mock的方法,根据定义,该方法不执行任何操作,除非您编写存根(given(...).willReturn(...)
)。即使是代码示例中的最后一个断言也是不必要的,因为validationInfo不会被validate
的默认模拟行为修改。
Mockito被设计用于测试合作者之间的交互,这已经通过您的验证verify(...)
完成了,因为您正在测试服务以及他与Validator
的协作。
测试用ValidationInfo
编写的消息与service
的范围无关
但是,您想要的是对Validator
进行单元测试,您应该在其中为验证消息编写特定的测试。看看下面的片段,我想象了ValidationInfo
API的一些部分:
public class ValidatorTest {
Validator validator = new Validator(); // not a mock
@Test
public void when_<some_part_of_the_domain>_is_incorrect_report_validation_error() throws Exception {
// given
Domain domain = createDomainWithInvalidData();
// when
validator.validate(domain, validationInfo);
// then
assertThat(validationInfo.getMessages()).isNotEmpty()); // not really necessary
assertThat(validationInfo.getLastMessage().text()).contains("<some_part_of_the_domain> is wrong");
}
}
请注意,我使用了BDD关键字(给定、何时、然后)来帮助我编写测试。Alos我使用了FestAssert库,它提供了很棒的断言工具。
这很有帮助。
我打破了几个小时的头脑,终于实现了我想要的。
我们需要做的是使用mockito的doAnswer
。请在下面找到片段:
doAnswer(new Answer<ValidationInfo> () {
@Override
public ValidationInfo answer (InvocationOnMock invocationOnMock) throws Throwable {
Object [] args = invocationOnMock.getArguments();
ValidationInfo vi = (ValidationInfo) args [1];
vi.getMessages.add("some message");
return vi;
}}).when(validator).validate (domain, validationInfo);
通过这种方式,我能够将自定义的失败消息放在列表中,这使得服务中的验证失败;这就是测试的全部目的。
早些时候,测试没有通过,因为在服务代码中对列表的大小进行了检查。列表大小始终为零,因为verify
只会验证该方法是否被调用,但不会实际更改对象的状态。
希望这能有所帮助。如果有什么更好的方法可以实现这一点,请发表评论。