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



如果输入字符串(在我的例子中是StringBuilder)超过2048个字符,我的方法将返回一个异常。由于我们不能使用Mockito模拟最终类,有人能告诉我如何进行单元测试吗。

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

IMHO,这里不需要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);
    }
}

Mockito的开发人员认为,传递mock似乎比创建真正的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,等等。根据它的逻辑结论,你可以想象一个非常高级的mock String对象,它会为你给它的任何调用正确地返回一个值。在这一点上,你已经从内到外重新实现了String,可能是以牺牲可读性为代价的,Java历史上最明显的实现,原因很少。如果有什么不同的话,那个对我来说似乎很愚蠢。

一些指导原则:

  • 不要嘲笑你不拥有的实现。例如,Mockito在其模拟的实现中仍然受到final标记的约束,因此嘲笑其他类型是一个坏习惯。当其他团队的实现细节可能会破坏你的测试时,这是一个非常糟糕的迹象

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

  • 特别是,不要模拟数据对象。例如,它们依赖于与setFoo匹配的getFoo,也可能依赖于equalshashCode的工作。在任何情况下,设计良好的数据对象都没有外部依赖关系。

  • 尽可能模拟接口而不是实现类。这将使您免受改变模拟工作方式的实现细节的影响。

  • 如果您发现自己的数据对象进行了服务调用,或者使用了不利于测试的资源,请记住,您可以重构类以提高可测试性,例如将服务方法提取到单独的定义良好的类中,或者提取接口并使自定义的可重用测试加倍。您的测试应该是类的一流用户,为了可测试性,更改您控制的类(或包装您不控制的类)是您的特权。我经常发现,这使得这些类在生产中更容易使用。

相关内容

  • 没有找到相关文章

最新更新