How can I mock Google's Geocoding API request using mockito/powermock?
Asked Answered
H

1

8

I want to unit test this method using mockito/powermock:

@Service
public class GoogleApiService {

    private static final Logger logger = LoggerFactory.getLogger(GoogleApiService.class);

    private static final String LANGUAGE = "es";

    private List<AddressType> addressTypes = Arrays.asList(
            AddressType.LOCALITY,
            AddressType.ADMINISTRATIVE_AREA_LEVEL_2,
            AddressType.ADMINISTRATIVE_AREA_LEVEL_1,
            AddressType.COUNTRY
    );

    @Autowired
    private GeoApiContext geoApiContext;

    public String getLocalityFromLatLng(LatLng latLng) throws Exception {
        logger.debug("getLocalityFromLatLng");

        GeocodingResult[] geocodingResults = GeocodingApi.newRequest(geoApiContext)
            .latlng(latLng)
            .await();
        for (GeocodingResult geocodingResult : geocodingResults) {
            AddressType addressType = geocodingResult.types[0];
            if (addressTypes.contains(addressType)) {
                return geocodingResult.formattedAddress;
            }
        }
        return StringUtils.EMPTY;
    }

}

this is what i have tried:

@PrepareForTest(GeocodingApi.class)
public class GoogleApiServiceUnitTest extends AbstractUnitTest {

    private static final Double LATITUDE = -38.010403;
    private static final Double LONGITUDE = -57.558408;

    @Mock
    private GeoApiContext geoApiContext;

    @InjectMocks
    private GoogleApiService googleApiService;

    @Test
    public void testGetLocalityFromLatLng() throws Exception {

        LatLng latLng = new LatLng(LATITUDE, LONGITUDE);
        GeocodingResult geocodingResult = new GeocodingResult();
        GeocodingResult[] geocodingResults = new GeocodingResult[] { geocodingResult };

        GeocodingApiRequest geocodingApiRequest = new GeocodingApiRequest(geoApiContext);
        geocodingApiRequest.latlng(latLng);

        mockStatic(GeocodingApi.class);

        // when(GeocodingApi.newRequest(geoApiContext))
           //  .thenReturn(geocodingApiRequest);

        // when(GeocodingApi.newRequest(geoApiContext).latlng(latLng))
           //  .thenReturn(geocodingApiRequest);

        when(GeocodingApi.newRequest(geoApiContext).latlng(latLng).await())
            .thenReturn(geocodingResults);

        String locality = googleApiService.getLocalityFromLatLng(latLng);

        assertThat(locality, is(notNullValue()));

        verifyStatic(times(1));
        GeocodingApi.newRequest(geoApiContext).latlng(latLng).await();

        verifyNoMoreInteractions(geoApiContext);
    }

}

i'm getting NullPointerException. Here is the stack trace:

java.lang.NullPointerException
at com.google.maps.PendingResultBase.await(PendingResultBase.java:56)
at com.beermap.server.unit.service.GoogleApiServiceUnitTest.testGetLocalityFromLatLng(GoogleApiServiceUnitTest.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

is in the await() call line

EDIT: this is how it looks now, it is still not working

btw, @RunWith(PowerMockRunner.class) is in AbstractUnitTest

@Test
public void testGetLocalityFromLatLng() throws Exception {

    LatLng latLng = new LatLng(LATITUDE, LONGITUDE);
    GeocodingResult geocodingResult = new GeocodingResult();
    geocodingResult.types = new AddressType[] { AddressType.LOCALITY };
    geocodingResult.formattedAddress = FORMATTED_ADDRESS;
    GeocodingResult[] geocodingResults = new GeocodingResult[] { geocodingResult };

    GeocodingApiRequest geocodingApiRequest = mock(GeocodingApiRequest.class);

    mockStatic(GeocodingApi.class);

    when(geocodingApiRequest.latlng(latLng)).thenReturn(geocodingApiRequest);
    when(geocodingApiRequest.await()).thenReturn(geocodingResults); // NPE here
    when(GeocodingApi.newRequest(geoApiContext)).thenReturn(geocodingApiRequest);

    String locality = googleApiService.getLocalityFromLatLng(latLng);

    assertThat(locality, is(notNullValue()));

    // verifyStatic(times(1));
    // GeocodingApi.newRequest(geoApiContext).latlng(latLng).await();

    // verifyNoMoreInteractions(geoApiContext);

}

and the stack:

java.lang.NullPointerException
at com.google.maps.PendingResultBase.makeRequest(PendingResultBase.java:79)
at com.google.maps.PendingResultBase.await(PendingResultBase.java:55)
at com.beermap.server.unit.service.GoogleApiServiceUnitTest.testGetLocalityFromLatLng(GoogleApiServiceUnitTest.java:56)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Thanks in advance

Hbeam answered 26/2, 2016 at 1:44 Comment(1)
Can you add the null pointer stack trace?Skyscraper
A
6

There are several mistakes here and it show that you have misunderstanding of mocking. Let analyse them:

At first, I hope it's copy-past mistake, you don't have @RunWith(PowerMockRunner.class), otherwise you'll get another exception.

Second point, this commented out line is right when(GeocodingApi.newRequest(geoApiContext)).thenReturn(geocodingApiRequest);. When static method of the GeocodingApithen you want to return geocodingApiRequest.

At last, but most important point, this line is incorrect: when(GeocodingApi.newRequest(geoApiContext).latlng(latLng).await()).thenReturn(geocodingResults);

Only one static call will be mocked here: GeocodingApi.newRequest(geoApiContext).

And instance of really object will be return, because you create a new instance GeocodingApiRequest geocodingApiRequest = new GeocodingApiRequest(geoApiContext);.

The latlng(latLng) method is called from really object. And it's really call, not mocked. But seems to me you also want to mock it.

Then let's mock it: GeocodingApiRequest geocodingApiRequest = mock(GeocodingApiRequest.class);

After. mock all calls that need to be mocked:

when(geocodingApiRequest.latlng(latLng)).thenReturn(geocodingApiRequest);
when(geocodingApiRequest.await()).thenReturn(geocodingResults);

Another mistake, but so important as previous and more obviously: required files isn't set for geocodingResult.

GeocodingResult geocodingResult = new GeocodingResult();
geocodingResult.types = new AddressType[]{ AddressType.LOCALITY};
geocodingResult.formattedAddress = "Some address";

The full working test:

@RunWith(PowerMockRunner.class)
@PrepareForTest({GeocodingApi.class, GeocodingApiRequest.class})
public class GoogleApiServiceUnitTest  {

    private static final Double LATITUDE = -38.010403;
    private static final Double LONGITUDE = -57.558408;

    @Mock
    private GeoApiContext geoApiContext;

    @InjectMocks
    private GoogleApiService googleApiService;

    @Test
    public void testGetLocalityFromLatLng() throws Exception {

        LatLng latLng = new LatLng(LATITUDE, LONGITUDE);
        GeocodingResult geocodingResult = new GeocodingResult();
        geocodingResult.types = new AddressType[]{ AddressType.LOCALITY};
        geocodingResult.formattedAddress = "Some address";

        GeocodingResult[] geocodingResults = new GeocodingResult[] { geocodingResult };

        GeocodingApiRequest geocodingApiRequest = mock(GeocodingApiRequest.class);
        when(geocodingApiRequest.latlng(latLng)).thenReturn(geocodingApiRequest);
        when(geocodingApiRequest.await()).thenReturn(geocodingResults);

        mockStatic(GeocodingApi.class);

        when(GeocodingApi.newRequest(eq(geoApiContext)))
          .thenReturn(geocodingApiRequest);

        String locality = googleApiService.getLocalityFromLatLng(latLng);

        assertThat(locality, is(notNullValue()));

        verifyStatic(times(1));
        GeocodingApi.newRequest(geoApiContext);

        // add verification other mock if you really need it

        verifyNoMoreInteractions(geoApiContext);
    }
}
Annora answered 26/2, 2016 at 7:41 Comment(7)
thanks, it helps, but is still not working, i'm still getting NPE in await() call, I edited my test (see edit in answer) and also copy/paste your solution, and both fails in the same line.Hbeam
Have you added the 'GeocodingApiRequest.class' to '@PrepareForTest'?Annora
oh, I forgot, but I added now and is still not working.. same errorHbeam
Hmm, I've tested my code before posting, so another guesswork what could be different, it's using 'Mockito.mock' instead 'PowerMockito.mock'.Annora
excellent! that was the trick! thank you very much! I will update the question for any other with the same problem. Thanks again!Hbeam
@ArthurZagretdinov Have you upgraded to the latest version of Powermock 3 and tried to do the same thing? I'm looking for answers as I'm getting NPE... github.com/powermock/powermock/issues/1078Inotropic
I've just solved the problem without using PowerMock. From Mockito 3.6 version it is possible to mock static methods. I switched from mockito-core to mockito-inline 3.12.0. Mine code looks the same as above, just another imports and it works fine.Ternan

© 2022 - 2024 — McMap. All rights reserved.