调用更改mockito中的mock对象



我有一个非常复杂的方法,我想测试它的行为(使用Mockito和JUnit)。这个方法将一个对象(让我们称其类型为State)作为输入,并且应该考虑一些不同的状态变量来决定其输出。

例如,考虑以下规范(sState类的模拟):

  1. 如果设置了s.varOne,则返回其值
  2. 否则,如果设置了s.varTwo,则返回该值
  3. 否则,如果设置了s.varThree,则调用s.update(s.varThree),然后返回s.varOne,它现在将有一个值(即使在第1阶段没有)
  4. 否则,抛出错误

为了正确测试用例3,我想设置s对象,以便s.varOnes.varTwo一开始都是未设置的,但如果(且仅当!)sut调用s.update(s.varThree),那么在那之后s.varOne会返回一些东西。

有没有一种好的方法可以在Mockito中设置这种行为?

我曾考虑为s.varOne设置一些返回值链,然后验证调用的顺序是否与输出的顺序相对应(当然还有sut的返回值是否正确),但这感觉很糟糕;如果我随后更改该方法以其他方式计算其返回值,即调用s.varOne不同次数但不更改其输出,那么即使功能相同,测试也将失败。

我的理想解决方案是,我可以为mock对象添加一些"延迟"设置,当sut调用s.update()方法时会运行该设置,但我无法找到实现这一点的方法。

这里有两个模拟状态更改的选项,一个是好的选项,另一个是更好的选项。正如tieTYT上面所指出的,最好的选择是解开State的一般契约:State是可变的,并且拥有不是简单的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时,这会变得特别棘手。如果这不简单,不要浪费时间把一个方桩钉进一个圆孔。

mock的一种更常见的模式是使用自定义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[],因此强制转换是必要的,而且有点危险,但在调用模拟方法时,可以同步进行各种断言和修改。

希望能有所帮助!

相关内容

  • 没有找到相关文章