如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
我感觉完全一样。。。更改一个方法的访问修饰符,以便能够运行测试,这对我来说是个坏主意。在我们公司,我们也进行了很多讨论,在我看来,测试私有方法的好方法是使用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()”的测试失败,那么很清楚在哪里查找错误。
其他回答
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()时,您将看到警告。
私有方法被公共方法使用。否则,它们就是死代码。这就是为什么要测试公共方法,断言公共方法的预期结果,从而断言它使用的私有方法。
在公共方法上运行单元测试之前,应该通过调试来测试私有方法。
也可以使用测试驱动开发来调试它们,调试单元测试,直到满足所有断言。
我个人认为最好使用TDD创建类;创建公共方法存根,然后使用预先定义的所有断言生成单元测试,这样在编写代码之前就可以确定方法的预期结果。这样,就不会走上使单元测试断言符合结果的错误道路。然后,当所有单元测试通过时,您的类是健壮的并满足要求。
我想测试私有方法的两个例子:
解密例程-我不会想让任何人都能看到它们为了测试,否则任何人都可以使用它们来解密。但他们是代码固有的、复杂的,并且需要始终工作(明显的例外是反射,在大多数情况下,当SecurityManager未配置为防止这种情况发生时,反射甚至可以用于查看私有方法)。为社区创建SDK消耗在这里,公众对完全不同的含义,因为是全世界都能看到的代码(不仅仅是我的应用程序内部)。我把如果我不这样做,请将代码转换为私有方法希望SDK用户看到它-我不要将其视为代码气味SDK编程是如何工作的。但是当然我还需要测试私有方法,它们在哪里我的SDK的功能生活。
我理解只测试“合同”的想法。但我看不出有人可以主张不测试代码,因为您的里程可能会有所不同。
因此,我的权衡涉及到用反射使JUnit测试复杂化,而不是损害我的安全性和SDK。
我感觉完全一样。。。更改一个方法的访问修饰符,以便能够运行测试,这对我来说是个坏主意。在我们公司,我们也进行了很多讨论,在我看来,测试私有方法的好方法是使用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库来帮助测试私有方法和字段。它的设计考虑到了Android,但它确实可以用于任何Java项目。
如果您有一些带有私有方法、字段或构造函数的代码,可以使用BoundBox。它正是你想要的。下面是一个测试示例,它访问Android活动的两个私有字段来测试它:
@UiThreadTest
public void testCompute() {
// Given
boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());
// When
boundBoxOfMainActivity.boundBox_getButtonMain().performClick();
// Then
assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}
BoundBox使测试私有/受保护的字段、方法和构造函数变得容易。你甚至可以访问被继承隐藏的东西。实际上,BoundBox打破了封装。它会让您通过反射访问所有这些内容,但在编译时会检查所有内容。
它非常适合测试一些遗留代码。小心使用。;)