Problem
在mock factory所產生出來的東西時,會希望寫成generic形式以reuse:
private <T extends Command> CommandCreator mockCommandCreator(T mockCommand){ CommandCreator cmdCreator = mock(CommandCreator.class); oReturn(mockCommand).when(cmdCreator); cmdCreator.createCommand(mockCommand.getClass()); return cmdCreator; }
然而,針對mockCommand去getClass通常會拿到以下結果:
class org.tonylin.mockito.BatchCommand$$EnhancerByMockitoWithCGLIB$$6f5074eb
也因此無法達到mock效果。本篇文章分享解決這個問題的過程。
Lib Version
我們所使用的powermockito與mockito版本大致如下: (powermock項目比較多,省略部分)
POWERMOCK_MOCKITO_VERSION = "1.6.6" POWERMOCK_API_MOCKITO_DEP = "org.powermock:powermock-api-mockito:$POWERMOCK_MOCKITO_VERSION" POWERMOCK_API_MOCKITO_COM_DEP = "org.powermock:powermock-api-mockito-common:$POWERMOCK_MOCKITO_VERSION" MOCKITO_VERSION = "1.10.19" MOCKITO_CORE_DEP = "org.mockito:mockito-core:$MOCKITO_VERSION" HAMCREST_VERSION = "1.3" HAMCREST_DEP = "org.hamcrest:hamcrest-library:$HAMCREST_VERSION"
How to?
GetSuperclass
首先我嘗試使用getSuperclass作法,假如目標類別是concrete class就可以達到我要的效果:
// concrete class BatchCommand mockCommand2 = mock(BatchCommandImpl.class); System.out.println(mockCommand2.getClass()); System.out.println(mockCommand2.getClass().getSuperclass()); // outut class org.tonylin.mockito.BatchCommandImpl$$EnhancerByMockitoWithCGLIB$$b692c9d5 class org.tonylin.mockito.BatchCommandImpl
但如果目標是interface取到的SuperClass會是class java.lang.Object。如果你要mock的目標只有concrete class,可以透過mockingDetails去區別input的是否為mock object:
Class<? extends BatchCommandImpl> mockType = null; if(mockingDetails(mockCommand).isMock()){ mockType = (Class<? extends BatchCommandImpl>)mockCommand.getClass().getSuperclass(); } else { mockType = mockCommand.getClass(); } doReturn(mockCommand).when(cmdCreator); cmdCreator.createCommand(mockType);
Update Libraries
因為前一個方法我並不滿意,因此我找尋網路資料看看是否有解。後來在這篇文章中,看到mockingDetails中應有提供getMockedType的功能;然而實際試驗後,發現這個API只有在2.x beta版本中出現(參考javadoc)。
最後在mockito issue中,發現可以透過mockingDetails.getMockCreationSettings去拿到我要的資料。因此我將mockito升級到以下版本:
MOCKITO_VERSION = "2.18.3" MOCKITO_CORE_DEP = "org.mockito:mockito-core:$MOCKITO_VERSION"
並撰寫以下程式碼實驗:
BatchCommand mockCommand = mock(BatchCommand.class); System.out.println(mockCommand.getClass()); System.out.println(mockCommand.getClass().getSuperclass()); System.out.println(mockingDetails(mockCommand).getMockCreationSettings().getTypeToMock());
結果出現以下例外: (只擷取有用部分)
Caused by: java.lang.NoClassDefFoundError: org/mockito/cglib/proxy/MethodInterceptor at org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.<init>(PowerMockMaker.java:43)
我推測是library版本問題並找一下資料,只要替換powermock-api-mockito為powermock-api-mockito2就可以解決:
POWERMOCK_MOCKITO2_VERSION = "1.7.4" POWERMOCK_API_MOCKITO2_DEP = "org.powermock:powermock-api-mockito2:$POWERMOCK_MOCKITO2_VERSION"
第二個方法目前可以正常使用。
留言
張貼留言