我无法找到解决我的 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();
但是当然:不能模拟/监视类 java.lang.ProcessBuilder... 因为它是一个最终类
(可能是我首先使用 PowerMock 的原因)
我有什么选择?
最佳答案
您可以设计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 进程。
您费了这么大的劲来提高覆盖率,这一事实凸显出您当前的开发流程是有问题的。覆盖率本身并不是一个目标,它是一个可以让您对代码充满信心的指标。如果您必须通过避免效果良好的 @PrepareForTest
来增强它,那么有什么意义呢?
关于java - 如何模拟 Final 类并进行代码覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53485165/