【问题标题】:I want to test a private method - is there something wrong with my design? [duplicate]我想测试一个私有方法——我的设计有问题吗? [复制]
【发布时间】:2012-02-18 07:14:16
【问题描述】:

所以我对软件测试非常陌生,并且正在考虑在我的一个应用程序中添加几个测试。我有一个公共方法 addKeywords() 一路调用私有方法 removeInvalidOperations()。这个私有方法调用外部 API,大约 50 行代码。因为我认为这是一个有点复杂的方法,所以我想通过调用 addKeyword() 方法来测试它而不必这样做。然而这似乎是不可能的(至少通过 JUnit 是不可能的)。

我查看的信息表明,测试私有方法的愿望可能是代码异味。有人建议这可能是一个迹象,表明应该将其重构为一个单独的类并公开。此外,还有一些建议,如果您真的需要,那么您可以对您的生产代码进行编辑,例如更改私有方法的可见性。

我真的不明白为什么我当前的代码设计有问题,但也不喜欢编辑生产代码以满足我的测试需求的想法。

正如我所说 - 我对测试很陌生,因此非常感谢任何帮助。另外,如果我可以提供任何进一步的信息来帮助解答,请告诉我。

【问题讨论】:

  • 我经常使用的一个快速解决方法是将访问权限从私有更改为默认(包)并评论 // package access for unit tests only. 这是否可以接受取决于您的编码标准以及您对其他程序员的信任程度要理解这种方法,即使包访问,也不是“真正的 API”。

标签: java unit-testing testing integration-testing


【解决方案1】:

我建议重构它。

我查看的信息表明测试的愿望 一些私有方法可能是代码异味。有人建议, 这可能表明这应该被重构为一个单独的类 并公开。

您在自己的问题中涵盖了支持和反对它的各种原因,您似乎很清楚这些论点。但是您有一个看起来相当复杂的方法,并且涉及外部 API。 这值得自己测试。removeInvalidOperations() 仍然可以是它所在类的私有方法,但它基本上会委托给另一个依赖项。

class YourClass
{
    private OperationRemover remover;

    public void addKeywords() {
        // whatever
        removeInvalidOperations();
    }

    private void removeInvalidOperations() {
         remover.remove();
    }
}

这为您带来了额外的好处,即能够在某些时候替换此依赖项,包括无需实际进行外部 API 调用即可测试您的 addKeywords() 方法,这将使测试该方法更容易。例如,OperationRemover 可以是一个接口,出于测试目的,您只需在其位置传入一个存根,而不是在生产中使用的具体版本。至于您的具体版本,您可以独立于现有课程中发生的情况为其编写测试。

我真的不明白为什么我当前的代码设计有问题, 但也不喜欢编辑我的生产代码以适应我的想法 测试需求。

更容易的可测试性是一个附带好处。换个角度看:您实际上在做的是使代码松散耦合和可扩展。上面,我们将对外部 API 的调用与可能需要使用结果的代码分开。外部 API 可能会改变。您可能会从一项服务转到另一项服务,但使用结果的代码不必关心。该类可以保持不变,只有实际发出调用的类需要修改(或替换)。

现实世界的例子:那一年是 2007 年,您在美国大型金融中心的一家银行工作。您的应用程序需要使用帐户信息。您的代码访问银行内部的某种 Web 服务,并以所需的形式获取所需的信息,然后继续进行处理。 2008 年,美国金融业崩溃,你的银行(濒临崩溃)被另一家银行吞并。您的应用程序不会受到影响,但现在您必须访问现存银行中已经存在的不同 API 以从那里获取帐户信息。使用此帐户信息的代码是否需要更改?不必要。它与以前的帐户信息相同,只是来自不同的来源。不,所有需要更改的只是调用 API 的实现。消费代码永远不必知道。

这种松散耦合也促进和促进测试的事实是一个好处。

【讨论】:

  • 感谢您提供如此详细的回答!这确实帮助我了解了这种方法背后的一些好处。
【解决方案2】:

如果是private,则不能将其视为应用程序 API 的一部分,因此对其进行测试确实是代码异味 - 当测试中断时,是否可以?

单元测试应该是面向功能的,而不是面向代码的。您测试的是功能单元,而不是代码单元。

不管哲学如何,你实现之外的一个类在不入侵 JVM 的情况下无法访问私有方法,所以你不走运 - 你要么必须更改方法的可见性,要么将 protected 测试 API 设为单元 -测试类扩展,或通过调用使用它的公共方法间接测试函数。

【讨论】:

    【解决方案3】:

    通常您不想测试私有方法,但也有例外。

    如果出现以下情况,您可能会想测试私有方法:

    1. 你没有仔细考虑如何测试私有方法 间接调用现有的公共方法。

    2. 你的类的 API 太不灵活了。公共方法需要更多 参数,或者一些私有方法需要公开。

    3. 你的类的 API 足够灵活,但在公共之下 方法 它有一些非常复杂的私有方法 下面。

    根据您的问题,您可能属于上述任何一种情况。

    对于(1),显然您应该首先尝试找到一种方法来使用现有的公共方法测试您的私有方法。

    对于 (2) 和 (3),单元测试不会告诉您您处于哪种情况。您需要做的是编写一些示例代码。作为Josh Bloch recommends,为您的 API 编写一些用例。您的 API 应该是满足您的用例所需的最小公共方法集。

    (3) 是可以测试私有方法的情况。有各种tricks for that。对于生产代码,这些比将您的方法公开给 API 用户(将其公开)更好,以便您可以测试它。或者将相关功能拆分为 2 个类,以便您对其进行测试。

    您可以根据information hiding 来思考,而不是根据不精确和主观的“代码气味”来思考。可能会改变的设计决策不应该暴露在你的公共 API 中。最好不要将可能更改的设计决策暴露给您的单元测试——这就是人们建议不要测试私有方法的原因。

    但是如果你真的认为对你的私有方法进行单元测试很重要,并且如果你不能通过公共方法充分地做到这一点,那么不要牺牲你的代码的正确性!测试私有方法。最坏的情况是你的测试代码比较混乱,当私有方法改变时你必须重写测试。

    【讨论】:

      【解决方案4】:

      如果你不想调用addKeywords(),也许你应该添加另一个公共方法testRemoveInvalidOperations(),它只是调用私有removeInvalidOperations()。您可以在以后删除测试。

      【讨论】:

      • +1 不错的主意。小改动:使 testRemoveInvalidOperations() 默认访问(包)并添加相关注释以阐明其目的仅用于单元测试。
      • -1 仅仅为了测试而在代码中戳洞绝不是一个好主意。你必须测试你拥有什么,而不是你想拥有什么。此外,好的测试往往需要好的设计。如果测试要你重构它,那可能是对的。测试一段复杂的代码是非常公平的。
      • 诚然,您必须确保在向公众发布文件之前删除测试代码。公开代码是个坏主意!
      猜你喜欢
      • 2013-11-26
      • 2011-03-18
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      相关资源
      最近更新 更多