powerMock和mockito使用

Nooooone發表於2020-08-16

powerMock和mockito

  • powermock和mockito都是做mock的框架,powermock在mockito的基礎上擴充套件而來,支援mockito的操作(也支援別的mock框架比如easyMock)。因此在maven引入powermock的時候,需要引mockito的包。powermock和mockito版本上要配合著使用。powermock在mockito的基礎上,擴充套件了對static class, final class,constructor,private method等的mock操作。慎用這些mock,因為在一個良好的設計裡,static final private這些class和method是不需要被測試的,會被public方法呼叫,只要測試public就好。

使用

  • maven引入
<properties>
    <powermock.version>2.0.2</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>
  • 和spring配合使用的時候,要特別注意版本的問題,如果測試起不來的話,先確認下powermock的版本和spring是否對應。

註解

  • @powermockIgnore,預設情況下,powermock試圖使用自己的classLoader去loader所有的class,除裡system class(java.lang等目錄下的class),使用powermockIgnore宣告的class,pwoermock也不會載入。

白盒測試

  • powermock提供了幾種方法來處理私有方法,私有變數,私有建構函式
  • Use Whitebox.setInternalState(..) to set a non-public member of an instance or class.
  • Use Whitebox.getInternalState(..) to get a non-public member of an instance or class.
  • Use Whitebox.invokeMethod(..) to invoke a non-public method of an instance or class.
  • Use Whitebox.invokeConstructor(..) to create an instance of a class with a private constructor.

mock建構函式

whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));

@RunWith(PowerMockRunner.class)
@PrepareForTest(X.class)
public class XTest {
        @Test
        public void test() {
                whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));

                X x = new X();
                x.y(); // y is the method doing "new MyClass()"

                ..
        }
}
  • prepare的時候,prepareForTest的類是呼叫MyClass的類。

Delegate to another JUnit Runner

  • spring和powermock結合使用時,使用以下聯合註解
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PrepareForTest(Test.class)
  • powerMock還是由自己的runner來做object的mock工作,在執行時,再交給delegate的runner去執行。
  • 不同的runner結合使用https://codete.com/blog/testing-spring-boot-application-with-junit-and-different-runners/

各種runner是何時如何起作用的呢

  • 首先runner是幹啥的?
    • runner其實就是各個框架在跑測試case的前後處理一些邏輯。
    • 比如在Junit框架中,我們什麼都不寫,用Junit預設的Runner BlockJUnit4ClassRunner來執行case,主要做什麼事情呢?就是處理Junit框架中的一些註解,比如掃到那些所有@Test的註解,這些是要跑的case,將那些@Ignore的註解的case忽略掉,在執行test case的前後,執行那些@Before和@after的註解的方法。
  • BlockJUnit4ClassRunner
 @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else {
            runLeaf(methodBlock(method), description, notifier);
        }
    }

    @Override
    protected Description describeChild(FrameworkMethod method) {
        return Description.createTestDescription(getTestClass().getJavaClass(),
                testName(method), method.getAnnotations());
    }

    @Override
    protected List<FrameworkMethod> getChildren() {
        return computeTestMethods();
    }
  • 主要是這三個方法,getChildren得到所有@Test註解的方法。runChild中,將要呼叫的方法組織好,最後通過反射呼叫這個方法執行,同時處理執行成功或者執行失敗的結果。

  • mockito的runner,JUnit44RunnerImpl,在跑test之前,將@Mock註解的物件構造出來。

  • SpringJUnit4ClassRunner 在test class中做依賴注入。

  • 總之,就是各個不同的runner在處理各自框架的職責。mockitorunner的就是負責mock,spring的runner就是負責依賴注入。

  • 這裡也就解釋了powermock的delegate是如何work的: testClass首先會交給powermockRunner完成自己的mock的工作,然後再交給springRunner去完成依賴注入的工作。

  • https://dzone.com/articles/understanding-junits-runner

  • https://codete.com/blog/testing-spring-boot-application-with-junit-and-different-runners/

參考

相關文章