我写了一个类来读取整个文件并返回内容。
class ClassToTest {
public methodToTest(String input) {
return privateMethod(input);
}
private privateMethod(input) {
ClassPathResource classPathResource = new ClassPathResource(input);
IOUtils.toString(classPathResource.getFile());
}
}
现在,在我的测试类中,我不希望我的测试实际读取文件,所以我试图模拟该方法classPathResource.getFile()
但不知何故,如果不编写PrepareForTests()
我就无法做到这一点,如果我这样做,这些测试不计入 JaCoCo。
我已经编写了测试用例
@Test
public void test_methodToTest() {
mockStatic(IOUtils.class);
when(IOUtils.toString(any()).thenReturn("DUMMY_STRING");
methodToTesT("file1.txt");
...
}
问题是IOUtils.toString
被正确模拟,但调用classPathResource.getFile()
尝试访问磁盘上的文件。为此,我可以这样做
PowerMockito.whenNew(ClassPathResource.class)
.withAnyArguments().thenReturn(mockedClassPath);
并将注释添加到我的测试类中
@PrepareForTest(ClassToTest.class)
class MyTestClass {
...
}
但现在的问题是这个测试类是从JACOCO测试覆盖率中跳过的。如何为此类编写测试?
你可以将模拟的引用传递给构造函数,这样做:
class ClassToTest {
private ClassPathResource classPathResource;
public ClassToTest(ClassPathResource classPathResource) {
this.classPathResource = classPathResource;
}
public methodToTest(String input) {
IOUtils.toString(classPathResource.getFile(input));
}
}
或者,您可以将模拟引用传递到执行此操作的方法中:
class ClassToTest {
public methodToTest(ClassPathResource classPathResource) {
IOUtils.toString(classPathResource.getFile());
}
}
不得不嘲笑私人成员应该被视为一种代码气味,并表明当前设计有问题。由于ClassPathResource
是在主题类内部初始化的,因此它现在与该类紧密耦合。虽然并非完全不可能模拟它确实使测试类变得更加困难。请考虑将类的创建作为依赖项反转为委托。
public interface PathResource {
String getFile(String input);
}
这将允许注入依赖项
class ClassToTest {
private classPathResource;
public ClassToTest (PathResource resource) {
this.classPathResource = resource;
}
public String methodToTest(String input) {
return privateMethod(input);
}
private String privateMethod(String input) {
return IOUtils.toString(classPathResource.getFile(input));
}
}
并且在测试时可以模拟/伪造/存根依赖项。
public void Test() {
//Arrange
//mock creation
PathResource resource = mock(PathResource.class);
String input = "path";
String expected = "expected_output";
//stubbing
when(resource.getFile(input)).thenReturn(expected);
ClassToTest subject = new ClassToTest(resource);
//Act
String actual = subject.methodToTest(input);
//Assert
verify(resource).getFile(input);
assertEquals(expected, actual);
}
在生产代码中,ClassPathResource
将从抽象派生
public class ClassPathResource implements PathResource {
//...code removed for brevity
}
它将与组合根的抽象相关联。
遵循上述建议现在就可以单独测试ClassToTest
,而不会对实施问题产生任何影响。