如何测试私有函数或者具有私有方法,字段或者内部类的类?

时间:2020-03-05 18:45:01  来源:igfitidea点击:

如何对具有内部私有方法,字段或者嵌套类的类进行单元测试(使用xUnit)?还是通过内部链接(在C / C ++中为" static")或者在私有(匿名)名称空间中私有化的函数?

仅仅为了能够运行测试而更改方法或者函数的访问修饰符似乎很糟糕。

解决方案

回答

测试私有方法的最佳方法是通过另一个公共方法。如果不能这样做,则满足以下条件之一:

  • 私有方法是无效代码
  • 我们正在测试的班级附近有设计异味
  • 我们尝试测试的方法不应该是私有的

回答

通常,单元测试旨在行使班级或者单元的公共界面。因此,私有方法是我们不希望显式测试的实现细节。

回答

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

回答

从本文开始:使用JUnit和SuiteRunner测试私有方法(Bill Venners),我们基本上有4个选择:

Don't test private methods.
  Give the methods package access.
  Use a nested test class.
  Use reflection.

回答

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

回答

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

you can't use reflection to get private methods from outside the owner class, the private modifier affects reflection also

这不是真的。正如Cem Catikkas的回答中所述,我们当然可以。

回答

如果我们使用的是旧版Java应用程序,并且不允许更改方法的可见性,那么测试私有方法的最佳方法是使用反射。

在内部,我们使用助手来获取/设置" private"和" private static"变量,以及调用" private"和" private static"方法。以下模式将使我们几乎可以执行与私有方法和字段相关的所有操作。当然,我们不能通过反射来更改"私有静态最终变量"。

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);
Notes:

  1. targetClass.getDeclaredMethod(methodName, argClasses) lets you look into private methods. The same thing applies for
  getDeclaredField.

  2. The setAccessible(true) is required to play around with privates.

回答

在尝试使用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:获取乐趣和收益的静态内部类

回答

如果我们要测试我们不愿或者无法更改的现有代码,则反射是一个不错的选择。

如果该类的设计仍然很灵活,并且我们想单独测试一个复杂的私有方法,则建议我们将其拉到一个单独的类中并分别测试该类。不必更改原始类的公共接口。它可以在内部创建helper类的实例并调用helper方法。

如果要测试来自辅助方法的困难错误条件,则可以更进一步。从帮助程序类中提取一个接口,在原始类中添加一个公共getter和setter来注入帮助程序类(通过其接口使用),然后将模拟的辅助程序类注入到原始类中,以测试原始类响应助手的异常。如果我们想测试原始类而不测试助手类,则此方法也很有用。

回答

如果要在无法更改代码的情况下测试旧版应用程序的私有方法,则Java的一个选项是jMockit,它使我们可以为对象创建模拟,即使它们是类的私有。

回答

当我的类中有私有方法非常复杂时,我觉得有必要直接测试私有方法,这就是代码的味道:我的类太复杂了。

解决这些问题的常用方法是挑逗一个包含有趣内容的新类。通常,此方法及其与之交互的字段,也许可以将一两个方法提取到新类中。

新类将这些方法公开为" public",因此可以进行单元测试。现在,新类和旧类都比原始类更简单,这对我来说很棒(我需要保持简单,否则我会迷路!)。

请注意,我并不是在建议人们不要动脑筋来创建课程!这里的重点是利用单元测试的力量来找到好的新类。

回答

正如以上许多建议所建议的,一个好方法是通过公共接口对其进行测试。

如果这样做,最好使用代码覆盖率工具(例如Emma)来查看私有方法是否实际上已从测试中执行。

回答

如果我们使用的是JUnit,请查看junit-addons。它具有忽略Java安全模型并访问私有方法和属性的能力。

回答

首先,我将把这个问题排除在外:为什么私人成员需要隔离测试?它们是否如此复杂,提供的行为如此复杂以至于需要进行公开测试以外的测试?这是单元测试,而不是"代码行"测试。不要汗流small背。

如果它们足够大,足够大,以至于这些私人成员各自都是复杂的"单位",请考虑将此类私人成员从此类中重构出来。

如果重构不合适或者不可行,在进行单元测试时,是否可以使用策略模式来替换对这些私有成员函数/成员类的访问?在单元测试中,该策略将提供添加的验证,但是在发行版本中,这将是简单的传递。

回答

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

因此,请勿测试私有方法。

回答

正如其他人所说的...不要直接测试私有方法。这里有一些想法:

  • 保持所有方法的小型化和重点化(易于测试,易于发现问题所在)
  • 使用代码覆盖率工具。我喜欢Cobertura(开心的一天,看起来好像有新版本发布了!)

在单元测试上运行代码覆盖率。如果我们发现方法未经过完全测试,请添加到测试中以扩大覆盖范围。争取100%的代码覆盖率,但要意识到我们可能不会明白。

回答

我们可以关闭Java访问限制以进行反射,以使private毫无意义。

setAccessible(true)调用可以做到这一点。

唯一的限制是ClassLoader可能会禁止我们这样做。

有关在Java中执行此操作的方法,请参见颠覆Java Access Protection以进行单元测试(Ross Burton)。

回答

在C中,我们可能使用过" System.Reflection",但在Java中我不知道。尽管我还是很想回答这个问题,因为如果我们"觉得需要对私有方法进行单元测试",我的猜测是还有其他地方是错误的...

我会认真考虑再次用新鲜的眼睛看我的建筑...

回答

如果测试类与应测试的类在同一包中怎么办?

当然,在另一个目录中,源代码为src和classs,测试类为test / src和test / classes。并让classestest / classes在类路径中。

回答

过去,我曾经使用反射来为Java做到这一点,我认为这是一个很大的错误。

严格来说,我们不应编写直接测试私有方法的单元测试。我们应该测试的是该类与其他对象之间的公共合同;我们永远不要直接测试对象的内部。如果另一个开发人员希望对该类进行小的内部更改,而这不会影响该类的公共合同,则他/她必须修改基于反射的测试以确保其有效。如果我们在整个项目中重复执行此操作,则单元测试将不再是对代码运行状况的有用衡量,并开始成为开发的障碍和开发团队的烦恼。

我建议做的是使用诸如Cobertura之类的代码覆盖工具,以确保我们编写的单元测试以私有方法提供对代码的适当覆盖。这样,我们可以间接测试私有方法的功能,并保持更高的敏捷性。

回答

我想在哪里测试私有方法的两个示例:

  • 解密例程-我不想让任何人都看到它们只是为了进行测试,否则任何人都可以使用它们来解密。但是它们是代码固有的,复杂的并且需要始终工作(明显的例外是反射,在大多数情况下,即使没有配置SecurityManager来防止这种情况,反射也可以用来查看私有方法)。
  • 创建一个供社区使用的SDK。这里的public具有完全不同的含义,因为这是全世界可能看到的代码(不仅仅是我的应用程序内部的代码)。如果我不希望SDK用户看到它,我会将代码放入私有方法中-我不认为这是代码的味道,只是因为SDK编程是如何工作的。但是,当然我仍然需要测试我的私有方法,而这些正是我的SDK功能真正存在的地方。

我了解仅测试"合同"的想法。但我看不出有人可以倡导实际上不测试代码,里程可能会有所不同。

因此,我的权衡包括通过反射使JUnit复杂化,而不是损害我的安全性和SDK。