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://codete.com/blog/testing-spring-boot-application-with-junit-and-different-runners/