我有一个非常复杂的方法,我想测试它的行为(使用Mockito和JUnit)。这个方法将一个对象(让我们称之为类型State
)作为输入,并且应该考虑一些不同的状态变量来决定它的输出。
作为示例,考虑以下规范(s
是State
类的模拟):
s.varOne
,则返回其值。s.varTwo
,则返回。s.varThree
,请致电s.update(s.varThree)
,然后返回s.varOne
,现在会有一个值(即使它没有在第1阶段。)为了正确测试案例3,我想设置s
对象,以便s.varOne
和s.varTwo
都不会开始,但是如果(并且仅当!)sut调用s.update(s.varThree)
,之后s.varOne
返回一些内容。
有没有一种很好的方法可以在Mockito中设置此行为?
我考虑为s.varOne
设置一些返回值链,然后验证调用的顺序是否与输出的顺序相对应(以及sut的返回值是否正确,当然),但这感觉很脏;如果我然后改变方法以其他方式计算其返回值,它调用s.varOne
不同的次数但不改变其输出,那么即使功能相同,测试也会失败。 / p>
我理想的解决方案是一种方法,我可以为模拟对象添加一些“延迟”设置,该设置在sut调用s.update()
方法时运行,但我无法找到实现该方法的方法
答案 0 :(得分:2)
您可以在此处模拟状态更改,这是一个不错的选择和更好的选择。最好的选择,如上面的 tieTYT 注释,只是为了解开State
的一般契约:State
是否真的有意义是可变的,并且有自我意识-mutating方法不是简单的setter?
好的选择(有时)是创建一个一次性子类 - 或者更好的是State
的接口实现。
@Test public void yourTest() {
SystemUnderTest sut = createSystemUnderTest();
State state = new State() {
@Override public void update() {
this.varOne = 42;
}
}
// rest of your test
}
那时,你根本不需要Mockito。但要注意:如果State有副作用或者难以实例化,这可能会有点棘手。你可以提取一个接口,这需要你把你的字段包装在getter中;你也可以使State成为一个命名的抽象类,然后传递mock(MyAbstractState.class, CALLS_REAL_METHODS)
,但是如果你认为没有初始化程序实际上在那个假实例上运行,那么这会变得特别毛茸茸,因此这些字段都是零或null。如果不简单,不要浪费时间将方形钉子钉入圆孔。
模拟的一种更常见的模式是使用自定义Answer
,您可以在其中以类型安全为代价执行任意代码。这是一个示例,假设update
为空,varThree
是整数:
@Test public void yourTest() {
SystemUnderTest sut = createSystemUnderTest();
final State s = Mockito.mock(State.class);
doAnswer(new Answer<Void>() {
@Override public Void answer(InvocationOnMock invocation) {
int actualVarThree = (int) invocation.getArguments()[0];
assertEquals(EXPECTED_VAR_THREE, actualVarThree);
s.varOne = actualVarThree;
return null;
}
}).when(s).update(anyInt());
// rest of your test
}
请注意,参数数组是Object[]
,因此强制转换是必要且有点危险,但是当您调用模拟方法时,您可以同步进行各种断言和修改。
希望有所帮助!