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

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


当前回答

我不确定这是否是一种好的技术,但我开发了以下模式来单元测试私有方法:

我不修改要测试的方法的可见性,也不添加其他方法。相反,我为要测试的每个私有方法添加了一个额外的公共方法。我调用这个额外的方法TestPort,并用前缀t_表示它们。然后,此测试端口方法只需访问相应的私有方法。

此外,我向测试端口方法添加了一个布尔标志,以决定是否从外部通过测试端口方法授予对私有方法的访问权。然后在静态类中全局设置该标志,在该类中我放置了例如应用程序的其他全局设置。因此,我可以在一个地方打开和关闭对私有方法的访问,例如,在相应的单元测试中。

其他回答

Android具有Android.support.annotation包中的@VisibleForTesting注释。

@VisibleForTesting注释表明,一个带注释的方法比通常情况下使方法可测试所需的方法更可见。此注释有一个可选的otherwise参数,如果不需要使方法在测试中可见,则可以指定该方法的可见性。Lint使用otherwise参数来强制实现预期的可见性。

实际上,这意味着您应该打开一个方法进行测试,@VisibleForTesting注释将显示警告。

例如

package com.mypackage;

public class ClassA {

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    static void myMethod() {

    }
}

当您在同一个包(com.mypackage)中调用ClassA.myMethod()时,您将看到警告。

私有方法只能在同一类中访问。因此,无法从任何测试类测试目标类的“私有”方法。一种方法是,您可以手动执行单元测试,也可以将方法从“私有”更改为“受保护”。

然后,受保护的方法只能在定义类的同一个包中访问。因此,测试目标类的受保护方法意味着我们需要在与目标类相同的包中定义测试类。

如果以上所有内容都不符合您的要求,请使用反射方式访问私有方法。

在Spring Framework中,您可以使用以下方法测试私有方法:

ReflectionTestUtils.invokeMethod()

例如:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

您可以使用PowerMockito为要测试的私有方法中调用/使用的私有字段和私有方法设置返回值:

例如,为私有方法设置返回值:

MyClient classUnderTest = PowerMockito.spy(new MyClient());

// Set the expected return value
PowerMockito.doReturn(20).when(classUnderTest, "myPrivateMethod", anyString(), anyInt());
// This is very important. Otherwise, it will not work
classUnderTest.myPrivateMethod();

// Setting the private field value as someValue:
Whitebox.setInternalState(classUnderTest, "privateField", someValue);

最后,您可以通过以下方式验证您的私有方法:

String msg = Whitebox.invokeMethod(obj, "privateMethodToBeTested", "param1");
Assert.assertEquals(privateMsg, msg);

Or

如果classUnderTest私有方法不返回值,但它设置了另一个私有字段,则可以获取该私有字段值以查看其设置是否正确:

// To get the value of a private field
MyClass obj = Whitebox.getInternalState(classUnderTest, "foo");
assertThat(obj, is(notNull(MyClass.class))); // Or test value

请参见下面的示例;

应添加以下导入语句:

import org.powermock.reflect.Whitebox;

现在,您可以直接传递具有私有方法、要调用的方法名和其他参数的对象,如下所示。

Whitebox.invokeMethod(obj, "privateMethod", "param1");