如何在单元测试中使用mockist方法避免误报?

时间:2010-09-30 14:13:22

标签: unit-testing refactoring mocking

由于我的应用程序域的数据结构最近变得非常复杂,我开始阅读模拟对象。很快我就想到了一个简单的问题,但到目前为止,答案已经证明是令人头痛的问题。所以这里:

我们有一个'Foo'类,其中'bar'是其方法之一:

class Foo {
    public String bar(int i){
        if(i == 1) return "arrr!";
    }
}

我们有一个班级海盗叫Foo.bar(1);在其中一种方法中:

class Pirate {
    public String yell(){
        Foo foo = new Foo();
        return foo.bar(1);
    }

现在我们在Pirate类的单元测试中模拟Foo类,因为Foo碰巧有很多其他依赖项:

@Test
public void returnsPirateString() {
    Pirate blackBeard = new Pirate();
    Foo fooMock = mock(Foo.class);
    fooMock.expectAndReturn("bar",1,"arrr!"); //expects 'bar' function to be called once and returns "arrr!"
    assertEquals(blackBeard.yell(),"arrr!");
}

现在发生的事情是,如果我们重构方法栏以返回null而不是“arrr!”,我们的测试将继续愉快地运行,而我们的程序不能按照我们想要的方式运行。这可能会导致可能的调试噩梦。

使用mockist方法而不是经典测试方法来进行单元测试,大多数情况下所有“辅助”对象都被模拟,只有被测对象仍未被模拟,所以前面提到的问题也经常发生。 / p>

在模拟时可以采取哪些措施来防止此问题?

3 个答案:

答案 0 :(得分:3)

在你的测试中,你正在测试使用Foo的Pirate类的yell()方法。所以你必须模仿Foo的bar方法的行为。 为确保您的bar方法正常运行,您需要另一个测试用例来测试Foo的bar方法。

@Test
public void testBar() {
   //make sure bar retrun "arrr"!
}

现在,如果bar方法返回null,则此测试用例将失败!

答案 1 :(得分:2)

你也应该单独测试'helper object'。一旦这两个都被覆盖和测试,那么你可以确保它们以预期的方式相互影响。

更改“辅助对象”应该通过针对该辅助对象的测试来确认它仍然按预期运行。

如果您担心帮助程序和主类的组合的特定运行时行为,那么您应该使用集成测试或更高级别的其他测试来断言这两个一起工作如预期的那样。

答案 2 :(得分:0)

测试returnsPirateString 不是误报 - 它正在测试当Pirate的{​​{1}}实例返回'arrr!'时会发生什么。

换句话说,当您测试Foo时, Pirate.yell 返回并不重要,除非它创建一个特殊的边界条件(您可能已经有一个测试文档记录Foo.baryell返回Foo时执行的操作。)

null不负责保证Pirate.yell的任何特定返回值,因此其单元测试不应指望任何特定的返回值。您甚至应该更改测试以使用其他内容比当前的Foo.bar返回值。