2013-06-25 71 views
2

我有一个相当复杂的方法,我想测试行为(使用Mockito和JUnit)。此方法将一个对象(我们称其类型为State)作为输入,并应考虑几个不同的状态变量来决定其输出。调用mockito中的模拟对象

作为一个例子,考虑下面的说明书(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不同的次数,但不会改变它的输出,那么即使功能相同,测试也会失败。

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

+2

关于您的测试的可疑之处在于您的第三个示例中您的模拟成为测试中的系统。你可以让模拟返回任何你想要的,但你想测试**模拟**返回一定条件下的东西。难道你不能重组你的代码,以便你的SUT是一件事而不是1.x事情吗? –

+0

这听起来像你在同一个测试用例中有多个断言一样。你能否把你的测试案例分解成更小的测试用例,每个测试用例只测试一个条件?你不应该在模拟中需要任何条件逻辑。如果您正在测试的代码将模拟调用两次,并且每次都要求不同的返回值,那么只需将多个值传递给您的'thenReturn'调用(如我的示例http://stackoverflow.com/questions/8088179/using -mockito与 - 多呼叫到所述-相同方法-用最同参数/ 8395685#8395685)。 –

+0

@DavidWallace:如问题所述,我不想创建一个返回值链,因为这会使得测试依赖于实现而不仅仅依赖于合约(即“必须获得varOne'两次,然后调用'update()',然后重新获得它,否则测试将失败,即使合同 - 也许 - 仍然是全满的)。 –

回答

2

你有几个选择来模拟状态变化,这是一个很好的选择和更好的选择。最好的选择,如上面的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。不过要注意的是:如果国家有副作用或难以实例化,这可能会有点棘手。你可以提取一个接口,这将需要你把你的领域包装在getter中;你也可以使State成为一个命名的抽象类,然后通过mock(MyAbstractState.class, CALLS_REAL_METHODS),但是当你认为没有初始化器实际运行在假实例上时,这会变得特别多毛,因此这些字段全部为零或空。如果不是那么简单,不要浪费你的时间来迫使一个方形的钉子进入一个圆孔。

一个更常见的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[],所以演员是必要的,稍有危险,但你可以做出种种断言并在您调用模拟方法时同步修改。

希望有帮助!

+0

答案技巧就是我最后的结果,'State'是一种不受我控制的类型,带有一堆副作用等,所以“不幸的是,“最好”的方法是不可用的为广泛的答案anks! –