关于java:我想测试一个私有方法 – 我的设计有问题吗?

I want to test a private method - is there something wrong with my design?

本问题已经有最佳答案,请猛点这里访问。

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

我所看到的信息表明,测试私有方法的愿望可能是代码味道。有些人认为这可能表明这应该被重构为一个单独的类并公之于众。此外,还有一些建议,如果您确实需要,则可以对您的生产代码进行编辑,例如:更改私有方法的可见性。

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

正如我所说 - 我对测试很新,所以任何帮助都非常感激。此外,如果有任何进一步的信息,我可以告诉我,以帮助解答。


我建议重构一下。

The information I have looked at is suggesting that the desire to test
ones private methods could be a code smell. Some people suggest that
it may be a sign that this should be refactored into a separate class
and made public.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
class YourClass
{
    private OperationRemover remover;

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

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

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

I don't really see why I have a problem with my current code design,
but also don't like the idea of editing my production code to suit my
tests needs.

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

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

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


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

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

无论哲学如何,实现之外的类都不能在不破坏JVM的情况下访问私有方法,所以你运气不好 - 要么必须改变方法的可见性,要么单元测试类扩展protected测试API ,或通过调用使用它的公共方法间接测试函数。


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

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

  • 您没有仔细考虑过如何测试私有方法
    通过调用现有的公共方法间接地。

  • 你班级的API太不灵活了。公共方法需要更多
    参数或某些私有方法需要公开。

  • 你班级的API足够灵活,但在公众面前
    它有一些相当复杂的私有方法
    下。

  • 根据您的问题,您可能处于任何一种情况。

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

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

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

    你可以用信息隐藏来思考,而不是用"代码气味"来思考,这是不精确和主观的。不应在您的公共API中公开可能更改的设计决策。优选地,可能改变的设计决策也不应暴露于您的单元测试 - 这就是人们建议不要测试私有方法的原因。

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


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