如何让 PowerMock 达到 Return 静态方法的预期值
How Can I Get PowerMock to Return the Expected Value from a Static Method
考虑我需要测试的 class 中的以下字段和方法。
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
UUID 存储在应用程序第一个 运行 上创建的文件中。 file.txt
存在于 pathToUUID
指示的位置。我正在尝试(并努力)为此方法编写单元测试。
@RunWith(PowerMockRunner.class)
@PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
@Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
不幸的是 when().thenReturn()
在测试期间未被调用,测试作为集成测试执行,从文件系统读取文件并返回其值,而不是我期望的模拟值。但是,如果我在测试方法中欺骗调用 Files.readAllBytes()
并将结果回显到控制台,则会显示 expected
值。
那么,我怎样才能让我的测试方法在 PowerMock when()-thenReturn()
模式下正常运行?
您需要测试的 class 写得不好。路径不应该是硬编码的——让它可以参数化——例如通过构造函数注入路径。然后,在您的集成测试中,只需注入测试资源的路径,您就可以开始了。没有 PowerMock,没有黑客 - 简单的构造函数注入。
JDK 类 使用 PowerMock 时很难处理。在你的情况下,我会这样做:
重构 UUIDGetter
以添加用于测试的构造函数,该构造函数接受 "uuid" 文件的路径:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
然后像这样测试它:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
@Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
资源文件(在测试资源文件夹中)名为 "so37059406/uuid.txt" 并包含(无行尾):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
这是恕我直言,更好,因为:
- 没有 powermock:它是一个强大的工具,但它是有代价的(测试速度较慢,可能测试奇怪的交互
- 更易读/更容易理解
对于遇到类似问题的任何人,我通过对我的测试进行以下更改解决了这个问题class:
@RunWith(PowerMockRunner.class)
@PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\DIRECTORY");
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
@Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
考虑我需要测试的 class 中的以下字段和方法。
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
UUID 存储在应用程序第一个 运行 上创建的文件中。 file.txt
存在于 pathToUUID
指示的位置。我正在尝试(并努力)为此方法编写单元测试。
@RunWith(PowerMockRunner.class)
@PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
@Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
不幸的是 when().thenReturn()
在测试期间未被调用,测试作为集成测试执行,从文件系统读取文件并返回其值,而不是我期望的模拟值。但是,如果我在测试方法中欺骗调用 Files.readAllBytes()
并将结果回显到控制台,则会显示 expected
值。
那么,我怎样才能让我的测试方法在 PowerMock when()-thenReturn()
模式下正常运行?
您需要测试的 class 写得不好。路径不应该是硬编码的——让它可以参数化——例如通过构造函数注入路径。然后,在您的集成测试中,只需注入测试资源的路径,您就可以开始了。没有 PowerMock,没有黑客 - 简单的构造函数注入。
JDK 类 使用 PowerMock 时很难处理。在你的情况下,我会这样做:
重构 UUIDGetter
以添加用于测试的构造函数,该构造函数接受 "uuid" 文件的路径:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
然后像这样测试它:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
@Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
资源文件(在测试资源文件夹中)名为 "so37059406/uuid.txt" 并包含(无行尾):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
这是恕我直言,更好,因为:
- 没有 powermock:它是一个强大的工具,但它是有代价的(测试速度较慢,可能测试奇怪的交互
- 更易读/更容易理解
对于遇到类似问题的任何人,我通过对我的测试进行以下更改解决了这个问题class:
@RunWith(PowerMockRunner.class)
@PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\DIRECTORY");
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
@Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}