如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
对于Java,我会使用反射,因为我不喜欢仅仅为了测试而更改对声明方法的包的访问。然而,我通常只测试公共方法,这也应该确保私有方法正常工作。
不能使用反射从所有者类外部获取私有方法,私有修饰符也会影响反射
这不是真的。你当然可以,正如塞姆·卡提卡斯的回答中所提到的。
其他回答
我感觉完全一样。。。更改一个方法的访问修饰符,以便能够运行测试,这对我来说是个坏主意。在我们公司,我们也进行了很多讨论,在我看来,测试私有方法的好方法是使用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()”的测试失败,那么很清楚在哪里查找错误。
这是我的龙目样本:
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Student student = new Student();
Field privateFieldName = Student.class.getDeclaredField("name");
privateFieldName.setAccessible(true);
privateFieldName.set(student, "Naruto");
Field privateFieldAge = Student.class.getDeclaredField("age");
privateFieldAge.setAccessible(true);
privateFieldAge.set(student, "28");
System.out.println(student.toString());
Method privateMethodGetInfo = Student.class.getDeclaredMethod("getInfo", String.class, String.class);
privateMethodGetInfo.setAccessible(true);
System.out.println(privateMethodGetInfo.invoke(student, "Sasuke", "29"));
}
@Setter
@Getter
@ToString
class Student {
private String name;
private String age;
private String getInfo(String name, String age) {
return name + "-" + age;
}
}
私有方法由公共方法调用,因此公共方法的输入也应测试这些公共方法调用的私有方法。当公共方法失败时,这可能是私有方法的失败。
如果您正在使用JUnit,请查看JUnit插件。它能够忽略Java安全模型并访问私有方法和属性。
测试私有方法的最佳方法是通过另一个公共方法。如果无法做到这一点,则以下条件之一为真:
私有方法是死代码您正在测试的类附近有设计气味您尝试测试的方法不应是私有的