如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?

仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。


当前回答

如果您只使用Mockito:

您可以将私有方法视为正在测试的公共方法的一部分。您可以确保在测试公共方法时覆盖私有方法中的所有情况。

假设您是一个仅限Mockito的用户(不允许或不想使用PowerMock或反射或任何此类工具),并且不想更改正在测试的现有代码或库,这可能是最好的方法。

如果选择这种方式,唯一需要处理的就是在私有方法中本地声明的变量(用户定义的对象)。如果私有方法依赖于本地声明的变量对象及其方法,请确保将这些用户定义的对象全局声明为私有对象,而不是本地声明的对象。您可以在本地实例化这些对象。

这允许您模拟这些对象并将它们注入回测试对象。你也可以模仿(使用when/then)他们的方法。

这将允许您在测试公共方法时无错误地测试私有方法。

优势

代码覆盖范围能够测试完整的私有方法。

缺点

对象的作用域如果您不希望对象公开给同一类中的其他方法,这可能不是您的方式。当在不同的公共方法和/或在同一方法中多次调用时,您可能会多次测试私有方法。

其他回答

测试私有方法的最佳方法是通过另一个公共方法。如果无法做到这一点,则以下条件之一为真:

私有方法是死代码您正在测试的类附近有设计气味您尝试测试的方法不应是私有的

我建议您稍微重构一下代码。当你不得不开始考虑使用反射或其他类型的东西来测试你的代码时,你的代码出了问题。

你提到了不同类型的问题。让我们从私有字段开始。对于私有字段,我会添加一个新的构造函数并将字段注入其中。而不是:

public class ClassToTest {

    private final String first = "first";
    private final List<String> second = new ArrayList<>();
    ...
}

我会用这个:

public class ClassToTest {

    private final String first;
    private final List<String> second;

    public ClassToTest() {
        this("first", new ArrayList<>());
    }

    public ClassToTest(final String first, final List<String> second) {
        this.first = first;
        this.second = second;
    }
    ...
}

即使使用一些遗留代码,这也不会成为问题。旧代码将使用一个空构造函数,如果你问我,重构后的代码看起来会更干净,而且你可以在测试中注入必要的值,而无需反射。

现在谈谈私有方法。根据我的个人经验,当您必须存根一个私有方法进行测试时,该方法在该类中没有任何作用。在这种情况下,一种常见的模式是将其包装在一个接口中,如Callable,然后在构造函数中传递该接口(使用多构造函数技巧):

public ClassToTest() {
    this(...);
}

public ClassToTest(final Callable<T> privateMethodLogic) {
    this.privateMethodLogic = privateMethodLogic;
}

我写的大部分内容看起来都是依赖注入模式。根据我的个人经验,它在测试时非常有用,我认为这种代码更干净,更容易维护。我对嵌套类也是这样说的。如果嵌套类包含大量逻辑,最好将其作为包私有类移动,并将其注入到需要它的类中。

在重构和维护遗留代码时,我还使用了其他几种设计模式,但这都取决于要测试的代码的情况。大多数情况下,使用反射并不是问题,但当您有一个经过严格测试的企业应用程序,并且在每次部署之前都要运行测试时,一切都会变得非常缓慢(这很烦人,我不喜欢这种东西)。

还有setter注入,但我不建议使用它。我最好使用构造函数,并在真正需要时初始化所有内容,留下注入必要依赖项的可能性。

从本文:使用JUnit和SuiteRunner测试私有方法(Bill Venners)中,您基本上有4个选项:

不要测试私有方法。授予方法包访问权限。使用嵌套测试类。使用反射。

对于Java,我会使用反射,因为我不喜欢仅仅为了测试而更改对声明方法的包的访问。然而,我通常只测试公共方法,这也应该确保私有方法正常工作。

不能使用反射从所有者类外部获取私有方法,私有修饰符也会影响反射

这不是真的。你当然可以,正如塞姆·卡提卡斯的回答中所提到的。

测试私有方法会破坏类的封装,因为每次更改内部实现时都会破坏客户端代码(在本例中是测试)。

所以不要测试私有方法。