代码之家  ›  专栏  ›  技术社区  ›  Kalle Richter

如何撤消/重置PowerMockito。模拟静态?

  •  4
  • Kalle Richter  · 技术社区  · 7 年前

    假设我有以下课程

    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() ?

    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 .

    某些例外情况 简单扩展 Exception .

    SSCCE位于 https://gitlab.com/krichter/powermock-undo-statik-mocking .

    我正在使用PowerMock 1.7.3。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Andrew S    7 年前

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

    但是我关于拆分测试的建议确实解决了这个问题。我不知道细节,但@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
  •  3
  •   Ryu S.    4 年前

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

    有一个 issue (参见Karl在accepted solution中的评论)解决了这个问题,解决方案是在方法级别使用@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修饰符,而不是方法