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

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


当前回答

我倾向于不测试私人方法。这里有疯狂。我个人认为,你应该只测试你公开的接口(包括受保护的和内部的方法)。

其他回答

在尝试了Cem Catikkas使用Java反射的解决方案后,我不得不说,他的解决方案比我在这里描述的更优雅。然而,如果您正在寻找使用反射的替代方案,并且能够访问您正在测试的源代码,那么这仍然是一个选项。

测试类的私有方法可能有好处,特别是在测试驱动开发中,您希望在编写任何代码之前设计小型测试。

创建一个可以访问私有成员和方法的测试,可以测试那些只访问公共方法而难以专门针对的代码区域。如果公共方法涉及多个步骤,它可以由多个私有方法组成,然后可以单独测试。

优势:

可以测试到更精细的粒度

缺点:

测试代码必须位于文件作为源代码更难维护与.class输出文件类似,它们必须保持在源代码中声明的相同包中

然而,如果连续测试需要这种方法,这可能是一个信号,表明应该提取私有方法,可以以传统的公共方式进行测试。

下面是一个复杂的例子,说明这是如何工作的:

// Import statements and package declarations

public class ClassToTest
{
    private int decrement(int toDecrement) {
        toDecrement--;
        return toDecrement;
    }

    // Constructor and the rest of the class

    public static class StaticInnerTest extends TestCase
    {
        public StaticInnerTest(){
            super();
        }

        public void testDecrement(){
            int number = 10;
            ClassToTest toTest= new ClassToTest();
            int decremented = toTest.decrement(number);
            assertEquals(9, decremented);
        }

        public static void main(String[] args) {
            junit.textui.TestRunner.run(StaticInnerTest.class);
        }
    }
}

内部类将编译为ClassToTest$StaticInnerTest。

另请参阅:Java提示106:静态内部类以获取乐趣和利润

我想测试私有方法的两个例子:

解密例程-我不会想让任何人都能看到它们为了测试,否则任何人都可以使用它们来解密。但他们是代码固有的、复杂的,并且需要始终工作(明显的例外是反射,在大多数情况下,当SecurityManager未配置为防止这种情况发生时,反射甚至可以用于查看私有方法)。为社区创建SDK消耗在这里,公众对完全不同的含义,因为是全世界都能看到的代码(不仅仅是我的应用程序内部)。我把如果我不这样做,请将代码转换为私有方法希望SDK用户看到它-我不要将其视为代码气味SDK编程是如何工作的。但是当然我还需要测试私有方法,它们在哪里我的SDK的功能生活。

我理解只测试“合同”的想法。但我看不出有人可以主张不测试代码,因为您的里程可能会有所不同。

因此,我的权衡涉及到用反射使JUnit测试复杂化,而不是损害我的安全性和SDK。

如果使用Spring,ReflectionTestUtils提供了一些方便的工具,可以帮助您以最小的工作量完成任务。例如,要在私有成员上设置模拟,而不必强制添加不需要的公共setter:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

在C++中:在包含包含要测试的私有函数的类头之前。

使用此代码:

#define private public
#define protected public

您可以创建一个特殊的公共方法来代理要测试的私有方法。使用IntelliJ时,@TestOnly注释是现成的。缺点是,如果有人想在公共环境中使用私有方法,他可以这样做。但注释和方法名会警告他。在IntelliJ上,执行此操作时将显示警告。

import org.jetbrains.annotations.TestOnly

class MyClass {

    private void aPrivateMethod() {}

    @TestOnly
    public void aPrivateMethodForTest() {
        aPrivateMethod()
    }
}