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

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


当前回答

要用大型而古怪的类测试遗留代码,能够测试我现在正在编写的一个私有(或公共)方法通常非常有用。

我在Java中使用junitx.util.PrivateAccessor包。它有很多用于访问私有方法和私有字段的有用的一行程序。

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

其他回答

在您的课堂上:

namespace my_namespace {
    #ifdef UNIT_TEST
        class test_class;
    #endif

    class my_class {
        public:
            #ifdef UNIT_TEST
                friend class test_class;
            #endif
        private:
            void fun() { cout << "I am private" << endl; }
    }
}

在单元测试类中:

#ifndef UNIT_TEST
    #define UNIT_TEST
#endif

#include "my_class.h"

class my_namespace::test_class {
    public:
        void fun() { my_obj.fun(); }
    private:
        my_class my_obj;
}

void my_unit_test() {
    test_class test_obj;
    test_obj.fun(); // here you accessed the private function ;)
}

在尝试了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:静态内部类以获取乐趣和利润

正如其他人所说。。。不要直接测试私有方法。以下是一些想法:

保持所有方法的规模和重点(易于测试,易于发现错误)使用代码覆盖工具。我喜欢Cobertura(哦,快乐的一天,它看起来像是一个新版本!)

在单元测试上运行代码覆盖率。如果您发现方法没有经过充分测试,请添加到测试中以提高覆盖率。目标是100%的代码覆盖率,但要意识到你可能无法做到这一点。

私有方法由公共方法调用,因此公共方法的输入也应测试这些公共方法调用的私有方法。当公共方法失败时,这可能是私有方法的失败。

如果您只使用Mockito:

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

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

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

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

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

优势

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

缺点

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