【问题标题】:How to undo/reset PowerMockito.mockStatic?如何撤消/重置 PowerMockito.mockStatic?
【发布时间】:2018-02-16 16:52:18
【问题描述】:

假设我有以下课程

public class StaticClass {

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

    private StaticClass() {
    }
}

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");
    }
}

我正在测试的

@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: How to unmock a method? 不适用,因为没有模拟引用传递给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。

【问题讨论】:

  • 单个测试做得太多 - 尝试拆分为多个测试,例如 testStaticMethodHappyPath()testStaticMethodWhenSomethingUnexpectedHappened()
  • 我是增强我的测试结构的建议,或者应该有助于解决问题。如果它解决了问题,我很乐意解释为什么会这样。

标签: java static mocking powermock powermockito


【解决方案1】:

我的意见,但一般来说,单元测试应该使用单一的代码路径。 (我认为这是将单一职责应用于测试方法。)

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

这些单独的测试有效:

@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();
}

【讨论】:

【解决方案2】:

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

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

所以在你的测试方法中你需要注释

/*
 * 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 装饰器放在类上而不是方法上

【讨论】:

    猜你喜欢
    • 2021-11-03
    • 1970-01-01
    • 2022-01-06
    • 2013-12-14
    • 2010-12-06
    • 1970-01-01
    相关资源
    最近更新 更多