我有一个带有公共方法Price
的类calculate()
,该方法调用了另一个私有方法calculateByA()
。
public class Price {
public SomeObject calculate() {
if(someCondition) {
calculateByA()
// ....
} else {
//Calculate something else
}
}
private int calculateByA() {
//calculation logic
}
}
我想进行单元测试以测试calculationByA()
的逻辑。
我想到了:
calculate()
进行测试,并将someCondition
设置为true
。 calculateByA()
更改为package-private,然后直接在测试中调用。但是,我不确定哪种方法更好。
答案 0 :(得分:1)
我非常不喜欢powermock等人的用法。问题不在于它们让您绕过了能见度系统(其他语言根本不需要理会,例如参见python),而是您尝试测试实现细节。
为可见的方法编写测试,并观察其私有方法的副作用。在您的示例中,例如在someCondition中测试,您的结果在这种情况下是否符合预期。 如果由于存在许多私有方法而遇到问题,请考虑使用一个程序包并拆分代码-无论如何它可能太大了。还可以使用DI luke!
答案 1 :(得分:0)
您的问题有三个选择。最终,取决于您的情况最适合您。但是,以下是一些有关如何选择它们的想法:
在您提到的情况下,可以使用正确的配置调用calculate
来执行calculateByA
。我相信这是最好的方法。该代码的结构意味着calculate
是访问calculateByA
的公共接口。这意味着您的测试将在行为发生变化时提醒人们注意问题。反过来,这意味着班级的用户将经历行为上的变化。
正如您提到的,这将允许您从同一程序包中的其他类(包括单元测试类)调用calculateByA
。当然,这是一种允许对calculateByA
进行简单且独立的单元测试的方法。但是,我不喜欢这样,仅仅是因为有一个原因,您只希望通过calculate
函数进行访问,并且通过更改访问权限,很容易使同一包中的其他类直接调用calculateByA
有许多技术可以让您直接在测试中调用私有方法。我不会重复@TiemoVorschütz的出色回答,其中详细介绍了如何执行此操作。但是,这将是我的最后选择。仅仅使用反射进行测试会更加脆弱,并增加测试套件的维护成本。
答案 2 :(得分:-1)
PowerMock可让您直接测试您的私有方法:
Price.java:
import java.util.concurrent.ThreadLocalRandom;
public class Price {
private final boolean someCondition;
public Price(final boolean someCondition) {
this.someCondition = someCondition;
}
public int calculate() {
if (someCondition) {
return calculateByA();
} else {
// Calculate something else
return 0;
}
}
private int calculateByA() {
final int randomNum = ThreadLocalRandom.current().nextInt(0, 10 + 1);
return 4711 * randomNum;
}
}
PriceTest.java:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Price.class)
public class PriceTest {
/*
* Refelection test for a single private method
*/
@Test
public void calculateByA() throws Exception {
final Price instance = new Price(true);
final int result = Whitebox.invokeMethod(instance, "calculateByA");
System.out.println(result);
}
/*
* Test public method calculate which invokes private method calculateByA
*/
@Test
public void testCalculate() throws Exception {
final Price instance = new Price(true);
System.out.println(instance.calculate());
}
}
JUnit4旁边的依赖项:
</properties>
<powermock.version>2.0.2</powermock.version>
</properties>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>