简体   繁体   中英

Issue with Mockito and Generics when using when + thenReturn

I've been looking a lot in the web about this issue, but none of the solutions I find is working for me.

I have a test class, and the service I'm attempting to test.

In the test class:

public class ElasticSearchSearchServiceImplTest{

@InjectMocks
private ElasticSearchSearchServiceImpl elasticSearchSearchService = new ElasticSearchSearchServiceImpl();

private SearchMyListCriteria searchMyListCriteria;

private SearchHitListCriteria searchHitListCriteria;

private SearchFieldLimitsCriteria searchFieldLimitsCriteria;

private SearchDetailCriteria searchDetailCriteria;

private SearchBrowseCriteria searchBrowseCriteria;

@Mock
private SearchRequestBuilder searchRequestBuilder;

@Mock
private SecurityService securityService;

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Client client;

@Mock
private ListenableActionFuture<SearchResponse> listenableActionFuture;

@BeforeMethod
public void setup() throws InterruptedException
{
    MockitoAnnotations.initMocks(this);
    SearchResponse sr = new SearchResponse();
    Mockito.when(client.prepareSearch(Mockito.anyString())
            .setQuery(Mockito.any(QueryBuilder.class))
            .addFields(Mockito.anyObject())
            .setFrom(Mockito.anyInt())
            .setSize(Mockito.anyInt())
            ).thenReturn(searchRequestBuilder);
    Mockito.when(securityService.isAccountabilityEnabled()).thenReturn(false);

    Mockito.when(searchRequestBuilder.execute()).thenReturn(listenableActionFuture);
    Mockito.when(listenableActionFuture.actionGet()).thenReturn(sr);

    searchMyListCriteria = buildSearchCriteriaBase();
    searchHitListCriteria = buildSearchHitListCriteria();
    searchFieldLimitsCriteria = buildSearchFieldLimitsCriteria();
    searchDetailCriteria = buildSearchDetailCriteria();
    searchBrowseCriteria = buildSearchBrowseCriteria();
}

@Test
public void testDoMyListSearch()
{
    SearchResponse searchResponse = new SearchResponse();
    Boolean execOk = false;
    try {
        searchResponse = elasticSearchSearchService.doMyListSearch(searchMyListCriteria);
        Assert.assertNotNull(searchResponse);
        execOk = true;
    }catch (Exception e){
        e.printStackTrace();
    }
    Assert.assertTrue(execOk);
}

Then, in ElasticSearchSearchServiceImpl.java , I have this method I'm calling within the test:

@Override
public SearchResponse doMyListSearch(SearchMyListCriteria criteria) throws Exception
{
    SearchTimer t = new SearchTimer();
    SearchResponse resp = null;
    int size = criteria.getDocIds().size();
    if(!criteria.getDocIds().isEmpty())
    {
        String indexName = getSearchAlias(criteria);
        String[] ids = criteria.getDocIds().toArray(new String[criteria.getDocIds().size()]);
        IdsQueryBuilder qb = QueryBuilders.idsQuery(getESTypes(criteria));
        qb.addIds(ids);

        SearchDataSourceAccountabilityEnum requiredAcc = securityService.isAccountabilityEnabled() && !criteria.getPermissions().isSuperUser()
                ? getAccountabilityForDataSources(criteria)
                : SearchDataSourceAccountabilityEnum.NONE;
        List<QueryBuilder> filters = new ArrayList<>();
        addSecurityToFilter(filters, criteria, requiredAcc);
        QueryBuilder filter = ElasticSearchUtils.and(filters);
        QueryBuilder fqb = filter == null ? qb : QueryBuilders.boolQuery().must(qb).filter(filter);

        SearchRequestBuilder req = client.prepareSearch(indexName)
                                         .setQuery(fqb)
                                         .addFields(determineMyListFieldsToReturn(criteria))
                                         .setFrom(0)
                                         .setSize(size);

        resp = req.execute().actionGet();
    }

    long searchMillis = t.getElapsedMillis();
    logPerform("Query Execution time for my list search " + criteria.toString() + COLON_SPACE + searchMillis + MILLISECONDS_MARKER);
return resp;
}

The thing is, that when it goes trough ElasticSearchSearchServiceImpl#doMyListSearch at line "resp = req.execute().actionGet();"it's throwing the following exception:

java.lang.ClassCastException: org.mockito.internal.creation.cglib.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$db3b9e0a cannot be cast to org.elasticsearch.action.search.SearchResponse
at com.sirsidynix.bcss.search.biz.svc.impl.ElasticSearchSearchServiceImpl.doMyListSearch(ElasticSearchSearchServiceImpl.java:228)
at com.sirsidynix.bcss.search.biz.svc.impl.ElasticSearchSearchServiceImplTest.testDoMyListSearch(ElasticSearchSearchServiceImplTest.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
at org.testng.SuiteRunner.run(SuiteRunner.java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1198)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1123)
at org.testng.TestNG.run(TestNG.java:1031)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:132)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:236)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:81)

I've tried most of the solutions in the web and nothing seems to be working for me. Please if you have suggestions I appreciate it.

Regards!

Did you try reducing the test to the smallest piece of code that causes the test to fail?

You think it's a problem with generics. If so, then something as simple as the following would also fail:

@Mock
private ListenableActionFuture<SearchResponse> listenableActionFuture;

@BeforeMethod
public void setup() throws InterruptedException {
    MockitoAnnotations.initMocks(this);
    SearchResponse sr = new SearchResponse();
    Mockito.when(listenableActionFuture.actionGet()).thenReturn(sr);
}

@Test
public void testDoMyListSearch() {
    SearchResponse sr = listenableActionFuture.actionGet();
}

Does it? My guess is not.. I'd think it's more likely due to the deep stubbing and the fact that the deep stubs return a mock of the same type as the mock you're returning in thenReturn . I wonder if using thenReturn there is even necessary, or if the deep stubbing would just make it work "magically".

Anyway, what I'm suggesting is to keep reducing the space to find where the problem lies. Also, removing unnecessary stuff like searchDetailCriteria would help people understand the code and help you faster.

(Sorry, I would prefer to comment but don't have enough reputation).

I think the Client returns a SearchRequestBuilder for some of the method calls. So you may need to mock a SearchRequestBuilder. Could you try to add the following before stubbing the client.

@BeforeMethod
public void setup() throws InterruptedException
{
MockitoAnnotations.initMocks(this);
SearchResponse sr = new SearchResponse();

SearchRequestBuilder requestBuilder = mock(SearchRequestBuilder.class, RETURNS_DEEP_STUBS);
when(client.prepareSearch(Matchers.any())).thenReturn(requestBuilder);
when(requestBuilder.setQuery(Matchers.any())).thenReturn(requestBuilder);
when(requestBuilder.addFields(Matchers.any())).thenReturn(requestBuilder);
when(requestBuilder.setFrom(Matchers.anyInt())).thenReturn(requestBuilder);
when(requestBuilder.setSize(Matchers.anyInt())).thenReturn(requestBuilder);

MockitoAnnotations.initMocks(this);
SearchResponse sr = new SearchResponse();
Mockito.when(client.prepareSearch(Mockito.anyString())
        .setQuery(Mockito.any(QueryBuilder.class))
        .addFields(Mockito.anyObject())
        .setFrom(Mockito.anyInt())
        .setSize(Mockito.anyInt())
        ).thenReturn(searchRequestBuilder);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM