如何让 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);
    }
}