Mockito:测试一个长字符串的方法

时间:2014-10-21 00:18:04

标签: java unit-testing mockito

如果输入String(在我的情况下为StringBuilder)超过2048个字符,则我的方法返回异常。既然我们不能使用Mockito模拟最终课程,那么有人能告诉我单元如何测试这个。

一种方法是实际传递一个看起来很愚蠢的字符串。有没有一种简单的方法来测试它?

2 个答案:

答案 0 :(得分:2)

恕我直言,这里不需要Mockito,只需使用创建已知长度的String(这里我使用commons-lang RandomStringUtils)。我还使用ExpectedException来测试异常

public class MyClassTest {
    private static int LIMIT = 2048;
    private static String TEST_STRING1 = RandomStringUtils.random(LIMIT);
    private static String TEST_STRING2 = RandomStringUtils.random(LIMIT + 1);

    @Rule
    public ExpectedException ee = ExpectedException.none();

    private MyClass myClass = new MyClass();

    @Test
    public void smallStringShouldBeOk() {
        myClass.myMethod("foobar"); // OK if no exception or assert on a returned value
    }

    @Test
    public void edgeCaseStringShouldThrow() {
        ee.expect(SomeException.class)
        ee.expectMessage("some message");

        myClass.myMethod(TEST_STRING1);
    }

    @Test
    public void tooLongStringShouldThrow() {
        ee.expect(SomeException.class)
        ee.expectMessage("some message");

        myClass.myMethod(TEST_STRING2);
    }
}

答案 1 :(得分:1)

Mockito的开发人员认为传递模拟似乎比创建真正的String更容易。但是,创建一个真正的字符串仍然是您测试的最明智的选择


您正确地注意到String是final,因此Mockito不能使用代理或字节码生成来静默创建它的子类。您将被迫使用Powermock,它实际上使用自定义类加载器重写您的测试类的字节码,以使用您的模拟String.length()而不是实际实现。无论哪种方式,模仿一个简单的字符串是一项非常繁重的工作,并且可能需要比替代方案更多的测试时间和内存。

即使String不是final,你仍然在写一个脆弱的测试,因为你(大概)只是在嘲笑String的一个子集。在这个假设中,你可以写:

when(mockString.length()).thenReturn(2048);

但是,有一天,您的实施转向:

public boolean isStringValid(String input) {
  // Handle Unicode surrogate pairs.
  return input.codePointCount(0, input.length()) <= 1024; // NOT 2048
}

...突然你的测试错误地通过了,因为Mockito看到了一个未被锁定的codePointCount并返回其默认的int返回值0。这是使用不完整的模拟进行测试的原因之一与使用真实对象的测试相比,它具有更低的弹性和更低的价值

但完全嘲笑呢?然后你可以模拟codePointCount,依此类推。根据它的逻辑结论,你可以想象一个如此高级的模拟String对象,它正确地返回一个值,绝对是你给它的任何调用。那时你已经重新实现了String的内部可能性,可能以牺牲可读性为代价,并且花费了很多工程时间来复制Java历史上最经过测试,最明显的实现之一。如果有的话, 对我来说似乎很愚蠢。

一些指导原则:

  • 不要嘲笑你不拥有的实施。例如,Mockito在它嘲笑的实现中仍然受final标记的约束,所以嘲弄其他类型是一个坏习惯。当其他球队&#39;实施细节可能会破坏您的测试,这是一个非常糟糕的迹象。

  • 当您可以使用真实的,经过测试的真实实例时,不要模拟对象。在这种情况下很难有理由嘲笑,这样做可能会使你的测试变得不必要 - 脆弱。

  • 特别是,不要模拟数据对象。例如,他们依靠getFoo匹配setFoo,也可能依赖equalshashCode工作。无论如何,精心设计的数据对象都没有外部依赖关系。

  • 尽可能模拟接口而不是实现类。这可以使您与实施细节隔离开来,从而改变您的模拟工作方式。

  • 如果您发现自己使用的数据对象进行服务调用,或者使用的资源不适合测试,请记住您可以重构类以提高可测试性 - 例如将服务方法提取到分离明确定义的类,或提取接口并生成custom reusable test doubles。您的测试应该是您班级的一流用户,并且为了可测试性,您有权更改您控制的课程(或包装您不能控制的课程)。我经常发现这使得这些课程在生产中更容易使用。