4

我有一个非常复杂的方法,我想测试它的行为(使用 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不同的次数但不更改其输出,那么即使功能相同,测试也会失败。

我理想的解决方案是一种可以为模拟对象添加一些“延迟”设置的方法,该设置在 sut 调用该s.update()方法时运行,但我无法找到实现该目标的方法。

4

1 回答 1

4

您有几个选项可以在这里模拟状态更改,一个不错的选项,一个更好的选项。正如上面tieTYT 所指出的,最好的选择就是解开 : 的一般合同:可变并且拥有不是简单设置器的自突变方法State真的有意义吗?State

好的选择(有时)是为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 设为一个命名的抽象类,然后 pass mock(MyAbstractState.class, CALLS_REAL_METHODS),但是当您考虑到没有初始化程序实际上在该假实例上运行时,这会变得特别棘手,因此这些字段都是零或 null。如果它不简单,请不要浪费时间将方形钉子钉入圆孔。

一个更常见的 mock 模式是使用 custom Answer,您可以在其中执行任意代码,但会牺牲类型安全。这是一个示例,假设update是 void 并且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
}

请注意,参数数组是 an Object[],因此强制转换是必要的并且有点危险,但是您可以在调用模拟方法时同步进行各种断言和修改。

希望有帮助!

于 2013-06-25T23:26:56.480 回答