我无法找到解决JUnit问题的解决方案,因此我已尝试将其简化到最大程度,以便希望它易于理解。
基本上,我正在尝试测试此类:
public class PB {
public int startProcessBuilder() {
int status = 1;
try {
ProcessBuilder pb = new ProcessBuilder("java", "-jar", ".....");
pb.directory(new File("/directory"));
Process process = pb.start();
status = process.waitFor();
} catch (IOException | InterruptedException e) {
System.out.println(e.getMessage());
}
return status;
}
}
所以我想到了这个测试:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {
private PB spyInstance = Mockito.spy(PB.class);
private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
private Process processMock = Mockito.mock(Process.class);
@Before
public void initialize() throws Exception {
PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
.thenReturn(processBuilderMock);
PowerMockito.doReturn(processMock).when(processBuilderMock).start();
}
@Test
public void testStartProcessBuilder() throws Exception {
assertThat(spyInstance.startProcessBuilder(), is(0));
}
}
我知道我的测试运行成功,但是在我所工作的公司中,我们正在使用jacoco和eclemma来显示代码覆盖率,并且这是一个已知的问题,如果使用此类,则所有代码均显示为0%覆盖率我们正在测试的位于@PrepareForTest批注中。
所以有一段时间,我们正在使用MockitoJUnitRunner(http://www.notonlyanecmplace.com/make-eclemma-test-coverage-work-with-powermock/)
@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {
private PB spyInstance = Mockito.spy(PB.class);
private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
private Process processMock = Mockito.mock(Process.class);
@Rule
public PowerMockRule rule = new PowerMockRule();
static {
PowerMockAgent.initializeIfNeeded();
}
@Before
public void initialize() throws Exception {
PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
.thenReturn(processBuilderMock);
PowerMockito.doReturn(processMock).when(processBuilderMock).start();
}
@Test
public void testStartProcessBuilder() throws Exception {
assertThat(spyInstance.startProcessBuilder(), is(0));
}
}
现在出现了真正的问题: 当我尝试运行测试时,出现此异常: org.mockito.exceptions.misusing.NotAMockException:传递给when()的参数不是模拟的!并显示以下行:
PowerMockito.doReturn(processMock).when(processBuilderMock).start();
是的,显然processBuilderMock不是模拟而是Powermock,所以我尝试替换了这两行
private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
PowerMockito.doReturn(processMock).when(processBuilderMock).start();
由此:
private ProcessBuilder processBuilderMock = Mockito.mock(ProcessBuilder.class);
PowerMockito.doReturn(processMock).when(processBuilderMock).start();
但是然后是:Cannot mock/spy class java.lang.ProcessBuilder... because it is a final class
(也许是我为什么首先使用PowerMock的原因)
我有什么选择?
答案 0 :(得分:2)
您可以将PB
类设计为易于测试。一种方法是提取ProcessBuilder
参数:
public class PB {
public int startProcessBuilder(String... args) {
try {
ProcessBuilder pb = new ProcessBuilder(args);
稍后,您可以使用小型“ Hello World”测试JAR:
new PB().startProcessBuilder("java", "-jar", "path-to-test-jar");
或使用标准echo
命令,无论使用什么操作系统,该命令都应具有相同的语法:
new PB().startProcessBuilder("echo", "Hello", "World");
您不需要模拟任何东西,实际上可以使用模拟JAR调用模拟Java进程。
要增加覆盖面会遇到很多麻烦,这一事实表明您当前的开发过程值得商question。覆盖范围本身并不是目标,它是使您对代码充满信心的度量标准。如果您必须避免使用@PrepareForTest
来提高效率,那有什么意义呢?