PowerMock:如何取消模拟一个方法?

17

我有一个静态方法,使用PowerMock进行模拟以抛出异常。(它删除文件。)不幸的是,在我的@After(每个测试之后)方法中,我需要调用这个没有mock的方法。我该如何取消模拟方法?

我没有找到类似于Mockito.reset()的方法。[参考:mockito : how to unmock a method?]

例子:

@RunWith(PowerMockRunner.class)
@PrepareForTest(PathUtils.class)  // Important: This class has a static method we want to mock.
public class CleaningServiceImplTest2 extends TestBase {

    public static final File testDirPath = new File(CleaningServiceImplTest2.class.getSimpleName());

    @BeforeClass
    public static void beforeAllTests() throws PathException {
        recursiveDeleteDirectory(testDirPath);
    }

    @AfterClass
    public static void afterAllTests() throws PathException {
        recursiveDeleteDirectory(testDirPath);
    }

    private File randomParentDirPath;
    private CleaningServiceImpl classUnderTest;

    @Before
    public void beforeEachTest() {
        randomParentDirPath = new File(testDirPath, UUID.randomUUID().toString()).getAbsoluteFile();
        classUnderTest = new CleaningServiceImpl(randomParentDirPath);
    }

    @After
    public void afterEachTest() throws PathException {
        recursiveDeleteDirectory(randomParentDirPath);
    }

    public static void recursiveDeleteDirectory(File dirPath) throws PathException {
        // calls PathUtils.removeFile(...)
    }

    @Test
    public void run_FailWhenCannotRemoveFile() throws IOException {
        // We only want to mock one method.  Use spy() and not mockStatic().
        PowerMockito.spy(PathUtils.class);

        // These two statements are tightly bound.
        PowerMockito.doThrow(new PathException(PathException.PathExceptionReason.UNKNOWN, randomParentDirPath, null, "message"))
            .when(PathUtils.class);
        PathUtils.removeFile(Mockito.any(File.class));

        classUnderTest.run();
    }
}
2个回答

17

我自己用了一段时间才弄明白,所以我来回答自己的问题。

据我所知,您需要“撤消”每个模拟对象。 Mockito.reset() 无法与 Class<?> 引用一起使用。 在测试方法的末尾添加以下内容:


// Undo the mock above because we need to call PathUtils.removeFile() within @After.
PowerMockito.doCallRealMethod().when(PathUtils.class);
PathUtils.removeFile(Mockito.any(File.class));

3

使用PowerMock取消模拟静态方法的唯一方法是在测试开始时模拟一个类,然后在测试结束时撤销模拟。无论您使用Spy还是常规模拟,都不重要。

测试使用:

"org.powermock" % "powermock" % "1.5" % Test,
"org.powermock" % "powermock-api-mockito" % "1.6.1" % Test,

测试类

package mytests;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.fest.assertions.Assertions.assertThat;


@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticTest.class})
public class TestTest {

    @Before
    public void checkIfOriginalMethodGetsCalled() {

//        PowerMockito.mockStatic(StaticTest.class); if you do this in @Before you are not going to be able to undo it
        assertThat(StaticTest.staticMethod()).isEqualTo("ORIGINAL VALUE");
        assertThat(StaticTest.otherStaticMethod()).isEqualTo("SPY TEST ORIGINAL");
    }

    @Test
    public void test1() {
        assertThat(StaticTest.staticMethod()).isEqualTo("ORIGINAL VALUE");
    }

    @Test
    public void test3_mocking() {
        mock(); // mock or spy static methods in a test, not in @Before 

        Mockito.when(StaticTest.staticMethod()).thenReturn("MOCKED VALUE");
        assertThat(StaticTest.staticMethod()).isEqualTo("MOCKED VALUE");
        assertThat(StaticTest.otherStaticMethod()).isEqualTo("SPY TEST ORIGINAL");

        undoMock(); // undo the mock at the end of each test, not in @After
    }

    private void mock() {
//        PowerMockito.mockStatic(StaticTest.class); both, spy and mockStatic work ok
        PowerMockito.spy(StaticTest.class);
    }

    private void undoMock() {
        PowerMockito.doCallRealMethod().when(StaticTest.class);
        assertThat(StaticTest.staticMethod()).isNull(); // the undo is going to work in the next test, not here yet. 
    }

    @Test
    public void test2() {
        assertThat(StaticTest.staticMethod()).isEqualTo("ORIGINAL VALUE");
    }

    @After
    public void checkIfOriginalMethodGetsCalled_AfterMockUndo() {

        // undoMock(); in @After  doesn't work with static methods
        assertThat(StaticTest.staticMethod()).isEqualTo("ORIGINAL VALUE");
        assertThat(StaticTest.otherStaticMethod()).isEqualTo("SPY TEST ORIGINAL");
    }
}

class StaticTest {
    public static String staticMethod() {
        return "ORIGINAL VALUE";
    }

    public static String otherStaticMethod() {
        return "SPY TEST ORIGINAL";
    }
}

确实帮了我很多。还有一个问题:为什么我们需要在撤销起作用之前调用 assertThat(StaticTest.staticMethod()).isNull(); // the undo is going to work in the next test, not here yet.? - valerii ryzhuk
我们肯定不需要进行isNull()断言。我不记得为什么在示例中这样做了。也许在doCallRealMethod()之后,如果调用该函数,则既不会调用模拟实现,也不会调用原始实现。我不记得了。 - maestr0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接