关于Java:如何撤消/重置PowerMockito.mockStatic?

How to undo/reset PowerMockito.mockStatic?

假设我有以下课程

1
2
3
4
5
6
7
8
9
public class StaticClass {

    public static void staticMethod() throws SomeException {
        System.out.println("staticMethod");
    }

    private StaticClass() {
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
public class SomeClass {

    public void someMethod() {
        try {
            StaticClass.staticMethod();
        }catch(SomeException ex) {
            System.out.println("SomeException occurred");
            return;
        }
        System.out.println("SomeException didn't occur");
    }
}

我正在测试的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticClass.class)
public class SomeClassTest {

    @Test
    public void testStaticMethod() throws Exception {
        mockStatic(StaticClass.class);
        doThrow(new SomeException("unimportant message")).when(StaticClass.class,
               "staticMethod");
        //test something where exception is needed
        SomeClass instance = new SomeClass();
        try {
            instance.someMethod();
            fail("IllegalStateException expected");
        }catch(IllegalStateException expected) {
        }
        //now test something where exception isn't needed
        instance.someMethod();
    }
}

如何撤消静态模拟/配置以抛出SomeException,以便可以在第二个instance.someMethod()中的try-catch块之后测试代码?

PowerMock:如何取消模拟方法?不适用,因为没有模拟引用传递给Mockito.reset,而传递StaticClass会导致java.lang.ClassCastException: java.lang.Class cannot be cast to org.mockito.internal.creation.bytebuddy.MockAccess

SomeException只是扩展Exception

在https://gitlab.com/krichter/powermock-undo-statik-mocking上提供了SSCCE。

我正在使用PowerMock 1.7.3。


我的看法是,但总体而言,单元测试应使用一条代码路径。 (我认为这是对测试方法应用单一职责。)

我关于拆分测试的建议确实可以解决问题。我不知道详细信息,但是@PrepareForTest为每个测试提供了一个新的StaticClass。

这些单独的测试有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Test
public void testStaticMethodWhenSomethingUnexpectedHappens() throws Exception {
    mockStatic(StaticClass.class);
    // changed exception type
    doThrow(new IllegalStateException("unimportant message")).when(StaticClass.class,"staticMethod");

    SomeClass instance = new SomeClass();
    try {
        instance.someMethod();
        fail("IllegalStateException expected");
    } catch (IllegalStateException expected) {
    }

    // added verification
    verifyStaticMethodWasInvokedOneTime();
}

@Test
public void testStaticMethodHappyPath() throws Exception {
    mockStatic(StaticClass.class);
    doNothing().when(StaticClass.class,"staticMethod");

    SomeClass instance = new SomeClass();
    instance.someMethod();

    // added verification
    verifyStaticMethodWasInvokedOneTime();
}

private void verifyStaticMethodWasInvokedOneTime() throws SomeException {
    verifyStatic(StaticClass.class);
    StaticClass.staticMethod();
}


对于任何想知道如何重置PowerMocks e.x的人。对于那些讨厌的私人静态最终记录器...

存在一个问题(请参见接受的解决方案中的Karl的评论),解决方案是在方法级别使用@PrepareForTest。

因此在您的测试方法中,需要注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * Test for MyClass.java which uses a private static final logger
 */

public class MyStaticMockTest {

  static final Logger logger = PowerMockito.mock(Logger.class);

  @BeforeClass
  public static void setup() {

    PowerMockito.mockStatic(LoggerFactory.class);
    PowerMockito.when(LoggerFactory.getLogger(MyClass.class))
        .thenReturn(MyStaticMockTest.logger);
  }


  @Test
  @PrepareForTest({LoggerFactory.class, Logger.class})
  public void testit() {
    MyClass mc = new MyClass();

    mc.methodWithSomeLogging();

    Mockito.verify(MyStaticMockTest.logger).info("some message");
  }

  @Test
  @PrepareForTest({LoggerFactory.class, Logger.class})
  public void testit() {
    MyClass mc = new MyClass();

    mc.anotherMethodWithSomeLoggingButUsingSameMessage();

    //Method will pass and not complain about info being called 2x
    Mockito.verify(MyStaticMockTest.logger, Mockito.times(1)).info("some message");
  }
}

如果要重置每个方法,只需将@PrepareForTest装饰器放在类上,而不是方法上