Mockito - Get Mock Class Type

在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效果。本篇文章分享解決這個問題的過程。

我們所使用的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"

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"

第二個方法目前可以正常使用。