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

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


当前回答

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()时,您将看到警告。

其他回答

PowerMockito就是为此而生的。

使用Maven依赖项:

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-core</artifactId>
        <version>2.0.7</version>
        <scope>test</scope>
    </dependency>

那你就可以了

import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);

测试私有方法的最佳方法是通过另一个公共方法。如果无法做到这一点,则以下条件之一为真:

私有方法是死代码您正在测试的类附近有设计气味您尝试测试的方法不应是私有的

一般来说,单元测试旨在使用类或单元的公共接口。因此,私有方法是您不希望显式测试的实现细节。

我感觉完全一样。。。更改一个方法的访问修饰符,以便能够运行测试,这对我来说是个坏主意。在我们公司,我们也进行了很多讨论,在我看来,测试私有方法的好方法是使用Java反射或其他框架,使方法可测试。对于复杂的私有方法,我多次这样做,这有助于保持测试的小型性、可读性和可维护性。

在我阅读了这里的所有答案之后,我只是不同意那些说“如果你需要测试私有方法,那么会有代码气味”或甚至“不要测试私有方法”的人。。。所以我给你举个小例子:

假设我有一个带有一个公共方法和两个私有方法的类:

public class ConwaysGameOfLife {

    private boolean[][] generationData = new boolean[128][128];

    /**
     * Compute the next generation and return the new state
     * Also saving the new state in generationData
     */
    public boolean[][] computeNextGeneration() {
        boolean[][] tempData = new boolean[128][128];

        for (int yPos=0; yPos<=generationData.length; yPos++) {
            for (int xPos=0; xPos<=generationData[yPos].length; xPos++) {
                int neighbors = countNeighbors(yPos, xPos);
                tempData[yPos][xPos] = determineCellState(neighbors, yPos, xPos);
            }
        }

        generationData = tempData;
        return generationData;
    }

    /**
     * Counting the neighbors for a cell on given position considering all the edge cases
     *
     * @return the amount of found neighbors for a cell
     */
    private int countNeighbors(int yPos, int xPos) {}

    /**
     * Determine the cell state depending on the amount of neighbors of a cell and on a current state of the cell
     *
     * @return the new cell state
     */
    private boolean determineCellState(int neighborsAmount, int yPos, int xPos) {}
}

因此,至少对于“countNeighbors”方法,我需要测试八个边缘情况和一些一般情况(直接位于角落的单元格、直接位于矩阵边缘的单元格和位于中间的单元格)。因此,如果我只是试图通过“computeNextGeneration”方法覆盖所有的情况,并且在重构之后,一些测试是红色的,那么识别错误所在的位置可能需要花费时间。

如果我分别测试“determineCellState”和“countNeighbors”,并且在重构和优化之后,“computeNextGeneration”和“determine CellState”的测试是红色的,那么我很确定错误将出现在“determiseCellState”方法中。

此外,如果您从一开始就为这些方法编写单元测试,这些测试将帮助您开发方法/算法,而无需考虑和包装公共方法中的其他方法调用和案例。你只需要在方法中编写快速的小测试来覆盖你的案例。。。例如,如果名为“countNeighbors_should_return_right_amount_of_noughbors_for_the_right_top_corner_cell()”的测试失败,那么很清楚在哪里查找错误。

从测试框架测试Java私有方法的最佳合法方法是在方法上添加@VisibleForTesting注释,因此相同的方法将像公共方法一样在测试框架中可见。