我该如何测试一个私人的功能或者一类,拥有私人的方法、字段或内班?

StackOverflow https://stackoverflow.com/questions/34571

  •  09-06-2019
  •  | 
  •  

我怎么单元的测试(使用来完成)一类是具有内部专用方法的领域或课程的嵌套?或者一个函数,是由私人通过具有 内部联系 (static 在C/C++)或是在私人(匿名的)namespace?

这似乎不好改变的接修改的方法或功能只是为了能够运行测试。

有帮助吗?

解决方案

更新:

一些10年后,也许最好的方式来测试一个私人的方法,或任何成员无法访问,是通过 @Jailbreak总管 框架。

@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;

这样你的代码类型安全和可读性。没有设计妥协,没有曝方法和领域,为了测试。

如果你已经有点传统 Java 应用程序,而你不允许改变的可见度的方法,最好的方式来测试私有方法是使用 反射.

在内部,我们正在使用的助手get/set privateprivate static 变量以及调用 privateprivate static 方法。以下模式将会让你这样做很多,任何关于私法和领域。当然,你不能改变 private static final 变量通过的反映。

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

和领域:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

注:
1. TargetClass.getDeclaredMethod(methodName, argClasses) 让你看看 private 方法。同样的道理也适用于 getDeclaredField.
2.的 setAccessible(true) 需要玩弄士兵。

其他提示

最好的方式来测试一个私人的方法是通过另一个公共的方法。如果无法做到这一点,则下列条件之一是真实的:

  1. 私方法是无用代码
  2. 有一个设计闻近类的你是测试
  3. 该方法正在试图测试不应该被私

当我有私人方法中的一类,都充分复杂的,我觉得有必要试验的私人方法是直接的,那是个代码闻:我的课上是太复杂了。

我平时的方法来处理这些问题是找出新的一类,其中包含的有趣的位。通常,这种方法和领域的相互作用,或许另一种方法或两个可以提取出一个新的类。

新类暴露了这些方法作为'公开',所以他们为单位的测试。新的和旧的课程现在这两种简单比原类,这是我(我需要让事情简单,或者我失去了!).

注意,我不是建议人们创建类不使用他们的大脑!这里的要点是利用部队单元的测试,以帮你找到良好的新课程。

我有用 反射 要做到这一Java过去,在我看来,这是一个很大的错误。

严格来说,你应该 是写单元的测试,直接测试私方法。什么你 应该 可以测试是公共合同的类具有与其他物品;你应该永远不会直接的测试对象的内部。如果另一个开发商愿意做一个小的内部改变级,这不会影响这类公共合同时,他/她然后有修改您的反射测试,以确保它的工作。如果你这么做多次在整个项目、单元测试,然后停止作为一个有用的测量的代码的健康,并开始成为一个阻碍发展,和一个烦恼的开发团队。

什么我让你做的,而不是使用一种代码复盖率的工具,如Cobertura,以确保该单元的测试你写提供体面的复盖面的码在私方法。这样,你是间接的测试是什么私人的方法这样做,并且保持较高水平的灵活性。

从这篇文章: 测试私有方法与JUnit和SuiteRunner (法案Venners),基本上你有4个选项:

  • 不要考验的私人方法。
  • 得到的方法包访问。
  • 使用一套测试类。
  • 使用的反思。

一般单元测试旨在行使公众接口的一类或单元。因此,私人的方法是执行细节,你不会想到测试。

只是两个例子,在那里我会想到测试一私法:

  1. 解密程序 -我不会 想让他们看到任何人看到的只是对于 为了测试,否则,任何人都可以 使用它们解密。但他们 征以代码,复杂的, 和需要一直工作(明显的例外是反映它可用于鉴甚至私人方法在大多数情况下,时 SecurityManager 是不是配置,以防止此)。
  2. 创建一个SDK 社区 消费。这里的公共需要 完全不同的含义,因为这个 是代码,整个世界可以看到 (不只是内部给我的应用程序)。我把 代码进入私人的方法如果我没有 想SDK用户能够看到它-我 不看到这个作为代码嗅觉,只是 如何SDK编程工作。但是的 我当然仍然需要测试我的 私有方法,他们是在那里 该功能的我SDK实际上 生活。

我明白这个想法的唯一测试的"合同"。但是我没有看到一个可以主张实际上不是测试的代码-你的里程可能会有所不同。

所以我折衷涉及到复杂的JUnits与反思,而不是影响我的安全和SDK。

私方法称为通过一个公共的方法,所以投入你的公共方法也应该试验私人的方法称为通过这些公众的方法。当一个公共的方法失败,那么,可能是一个失败的私方法。

另一种办法,我已经使用是要改变私人方法,包的私人或保护,那么补充的 @VisibleForTesting 注释的番石榴图书馆。

这会告诉任何人使用这种方法来采取谨慎和不直接访问它甚至在一个软件包。还有一个测试类不需要在相同的封装 身体, 但在同一封装下 测试 文件夹。

例如,如果一方法进行测试是在 src/main/java/mypackage/MyClass.java 那么你的测试呼吁应当被放置在 src/test/java/mypackage/MyClassTest.java.这样,你有访问的测试方法在你的测试类。

测试的遗产代码与大和古怪的课,这往往是非常有用,可以测试一个私人(或公共)方法我写的 现在.

我用的 junitx.工具.PrivateAccessor-包Java。许多有用的一套为私人访问的方法和私人领域。

import junitx.util.PrivateAccessor;

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

具有试图Cem Catikkas' 解决方案使用反射 Java,我不得不说他是一个更优雅的方案,比我已经在这里描述。但是,如果你是在寻找替代使用反射,并访问源的你是测试,这将仍然是一种选择。

有可能值得在测试私有方法的一类,特别是与 测试驱动的发展, ,哪里,你会喜欢的设计的小测试之前,你写任何代码。

创建一个测试与私人成员和方法,可以测试地区的代码这是困难的目标特别访问只向公众的方法。如果一个公开的方法有几个步骤所涉及的,它可以由若干私人的方法,然后可以单独进行测试。

优点:

  • 可以测试一个更好的粒度

缺点:

  • 测试代码必须居住在相同的 文件的源代码,可以 更难以维持
  • 同样地。类输出的文件,他们必须保持在相同的包宣布源代码

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

这里是一个复杂的例子怎么这会的工作:

// 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:静态的内部类为有趣和利润

正如其他人所说的...不要考验的私人方法。这里有几个想法:

  1. 保留所有方法小和集中的(轻松的测试,很容易找到什么是错的)
  2. 使用代码复盖率的工具。我喜欢 Cobertura (哦,快乐的一天,看起来像一个新版本出来!)

运行代码复盖面在该单元的测试。如果你看到的方法是不充分的检验加以测试,以获得复盖范围。目标是100%复盖代码,但意识到你可能不会得到它。

私人的方法有所消耗的公共的。否则,他们就死定了代码。这就是为什么你的测试的公共方法,声称的预期结果的公共方法和由此,私方法的消耗。

测试私有方法应该进行测试通过试运行之前单元测试对公众的方法。

他们还可调试使用测试驱动的发展、调试单元的测试,直到所有主张都得到了满足。

我个人认为它是更好地创建类借;创造公共方法存根,然后将产生单元的测试 所有 断言界定,在提前,因此预期结果的方法确定之前,你的代码。这样,就不要走错误的路径制作的单元试验的主张适应的结果。你的类是那么强大的和符合要求时,所有单元的测试通过。

弹簧框架 你可以测试私有方法的使用这种方法:

ReflectionTestUtils.invokeMethod()

例如:

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

如果使用弹簧, ReflectionTestUtils 提供一些方便的工具,帮助在这里与最小的努力。例如,设立一个拟在一个私人部件而没有被强制加入一个不良的公共设置器:

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

如果你想测试现有的编码,你不愿意或无法改变,反映的是一个好的选择。

如果该类的设计仍然是灵活的,你已经有了一个复杂的私人方法,你想测试分开,我建议你把它拔出来成为单独的类和测试这一类别。这并没有改变公众的接口原类;它可以在内部创建的一个实例,帮助类和呼吁帮助的方法。

如果你想测试困难的错误的条件来自的辅助方法,可以更进一步。提取口从的帮助类,添加一个公共吸气和二传到原来流入的助手类(用于通过其接口),然后再注入模型版本的助手类成原来的类测试如何将原有级应对异常的帮手。这种做法也是有益的,如果你想测试的原类还没有测试的辅助课。

测试私有方法打破了封装你的班级,因为每次你改变内部实现你打破客户代码(在这种情况下,检验)。

所以不要考验的私人方法。

的答案 JUnit.org 常问问题网页:

但如果你必须...

如果您使用的是JAVA1.3或更高,可以使用反颠复 访问控制机制的帮助 PrivilegedAccessor.详细信息关于如何使用它,阅读 这篇文章.

如果您使用的是JAVA1.6或更高和你注释你的测试 @测试,可使用 Dp4j 注射在你的测试方法。对于 详细信息关于如何使用它,看到 这个测试的脚本.

P.S.我的主要捐助者 Dp4j, ,问问 如果你需要帮助。:)

如果你想测试私有方法的一个遗产应用程序在这里你可以不改变法,一的选择是 jMockit, ,这将允许您可以创建嘲笑的对象,甚至当他们的私人类。

我倾向于不要试验私方法。还有就是疯狂。就个人而言,我认为你应该只考验你的公开暴露接口(包括保护和内部方法)。

如果你使用JUnit,看一看 junit-插件.它能够忽略的Java安全模式和私人访问的方法和属性。

一个私人的方法是只要访问的内的同一类。所以没有办法来测试一个"私人"方法的一个目标类从任何测试类。一的出路是,可以执行单位测试手动或可以改变你的方法从"私人",以"保护"。

然后一个受保护的方法可在相同的包装类定义。所以,检验受保护的方法的一个目标类意味着我们需要界定测试的类在同一个软件包作为目标的类。

如果所有上述不适合您的要求,使用 反射方式 访问私方法。

我建议你重构你的代码一点点。当你开始思考有关使用反射或者其他种类的东西,只是测试你的代码,什么是错误您的代码。

你提到的不同类型的问题。让我们开始与私人领域。在情况下的私人领域,我会加入一个新的构造和注的领域。而不是这样的:

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;
}

主要是我所写的看起来就像是一个依赖注射的模式。在我的个人经验,这是真正有用的,同时测试,并且我认为,这样的代码的更清洁和将更易于维护。我会说同样的关于嵌套的课程。如果一套类含有沉重的逻辑,它会更好,如果你移动了它作为一揽子的私人类和已注入一类的需要。

还有一些其他的设计图案我用,同时重构和维护传统代码,但是这一切都取决于案件的代码进行测试。使用反映大多不是一个问题,但是当你有一个企业应用程序,这在很大程度测试和运行测试之前的每一个部署一切都变得非常缓慢(它只是讨厌而且我不喜欢那样的东西).

还有注射器,但我不会推荐使用它。我最好坚持使用一个构造方法和初始化的一切时,这真的很必要的,留下的可能性注入必要的依赖关系。

作为许多上述建议,一个很好的方式就是来测试他通过你的公共接口。

如果你这样做,这是一个好主意,使用代码复盖率的工具(喜欢艾玛),看看你的私人方法实际上正在执行自己的测试。

这里是我的通用功能测试私人领域:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

请参见下文的例子;

以下进口的语句应添加:

import org.powermock.reflect.Whitebox;

现在你可以直接通过的对象,它拥有私人的方法,方法名称,并附加参数如下。

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

今天,我推Java库,以帮助测试私有方法和领域。它被设计成在头脑,但是它真的可以被用于任何Java项目。

如果你得到了一些代用方法或私人领域或构造,可以使用 BoundBox.这不正是你所寻找的。下面是一个例子的一个测试,访问两个私人领域的一个机器活动来测试它:

@UiThreadTest
public void testCompute() {

    // Given
    boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());

    // When
    boundBoxOfMainActivity.boundBox_getButtonMain().performClick();

    // Then
    assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}

BoundBox 使得易于测试的私人/保护的领域、方法和构造.你甚至可以访问东西就是隐藏的通过继承。事实上,BoundBox打破封装。它会给你接入所有通过反射, 一切都检查在编制时间。

它是理想的测试的一些遗留的代码。使用它仔细。;)

https://github.com/stephanenicolas/boundbox

首先,我将把这个问题:为什么你的私有成员需要孤立的测试?他们是复杂的,提供这种复杂的行为,如同需要测试除了公开表面?它的单元的测试,而不是'代码行'的测试。不要出汗的小东西。

如果他们那么大,足够大,这些私有成员的每一个"单元"的大型的复杂性--考虑重构这样的私人会员的此类。

如果重组不适当或不可行的,可以使用的战略模式来代替访问这些私功能的构件/部件的类当根据单元的测试?在单元的测试,该战略将提供额外的验证,但在释放建立这将是简单传递.

最近,我有这个问题,并写了一个小小的工具,叫做 Picklock, 那可避免的问题的明确使用Java反射API,两个例子:

调用的方法,例如 private void method(String s) -通过Java反射

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

调用的方法,例如 private void method(String s) -通过Picklock

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

设置领域,例如 private BigInteger amount; -通过Java反射

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

设置领域,例如 private BigInteger amount; -通过Picklock

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));

Java我会用 反射, 因为我不喜欢这个想法的改变访问的包装上的宣布方法,只是为了测试。然而,我通常只是测试的公共方法,还应确保私人的方法是否正常工作。

你不能使用反射来获得私人的方法以外的所有人类、私修改影响也反射

这是不正确的。你当然可以,如前所在 Cem Catikkas的答案.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top