Search code examples
javaspring-bootunit-testingresttemplate

Error: java.lang.NullPointerException by RestTemplate exchange method - SpringBoot


I am working on Spring Boot project with microservice architecture. I have a service that is talking to another service via RestTemplate. HttpDataClient.java class is sending dataId to external service and should receive something in response. For my test I should test that RestTemplate and check if I am getting good response.

Here is class that I need to test:

  public class HttpDataClient implements DataClient{
    
        private final static Logger LOGGER = LoggerFactory.getLogger(HttpDataClient.class);
    
        private final RestTemplate restTemplate;
        private final ObjectMapper objectMapper = new ObjectMapper();
    
        public HttpDataClient(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
    
        @Override
        public DataResponse getData(String dataId) {
            try{
                JsonNode node = restTemplate.exchange(
                        String.format("/data/{0}", dataId),
                        HttpMethod.POST,
                        new HttpEntity<>(buildRequest(dataId), headers()),
                        JsonNode.class
                ).getBody();
                return dataResponse(node);
            }catch (HttpStatusCodeException e) {
                String msg = String.format(
                        "Error getting data for dataId: {0}",
                        dataId,
                        e.getStatusCode(),
                        e.getResponseBodyAsString());
                LOGGER.error(msg);
                return dataResponse.failed();
            }
        }
    
        private MultiValueMap<String, String> headers() {
            final LinkedMultiValueMap<String, String> mv = new LinkedMultiValueMap<>();
            mv.set(HttpHeaders.CONTENT_TYPE, "application/json");
            return mv;
        }
    
        private DataResponse dataResponse(JsonNode node) {
            return DataResponse.dataResponse(
                    asString(node, "dataId"),
                    asString(node, "author"),
                    asString(node, "authorDataId"),
                    asString(node, "serverSideDataId")
            );
        }
    
        private JsonNode buildRequest(String dataId) {
            ObjectNode root = objectMapper.createObjectNode();
            root.put("dataId", dataId);
            return root;
        }
    }

Test class looks like this:

@RunWith(MockitoJUnitRunner.class)
public class HttpDataServiceTest {

    @Mock
    RestTemplate restTemplate;

    @InjectMocks
    private HttpDataService httpDataService;

    @Test
    public void getData() {

        httpDataService.getData("gameIdTest");
        Mockito
            .when(restTemplate.exchange(
                    ArgumentMatchers.eq("/game/IdTest"),
                    ArgumentMatchers.eq(HttpMethod.POST),
                    ArgumentMatchers.any(),
                    ArgumentMatchers.<Class<DataResponse>>any()))
            .thenReturn(new ResponseEntity<>(HttpStatus.ACCEPTED));
    }
}

When I run the test I am getting a NullPointerException

java.lang.NullPointerException at com.example.gamedata.HttpDataService.getData(HttpDataService.java:37) at com.example.data.HttpDataServiceTest.getData(HttpDataServiceTest.java:36) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

What am I missing here?


Solution

  • At least these are wrong:

    • you need to do Mockito.when() stuff before you call the actual method. Not after.
    • /game/idTest/ is different from /data/{0}, they won't match but they need to in order for this to work
    • DataResponse is not JsonNode, they should match, too
    • in your when() call you would actually need to return something sensible to be received in HTTP body, just "Accepted" is not enough and it leaves the response body empty
    • you would need to provide a reasonable json node as response

    So your test method contents should be something like

        // create response object
        ObjectNode responseNode = JsonNodeFactory.instance.objectNode();
        responseNode.put("dataId", "");
        responseNode.put("author", "");
        responseNode.put("authorDataId", "");
        responseNode.put("serverSideDataId", "");
        
        // prepare your mock for the call
        Mockito
            .when(restTemplate.exchange(
                    ArgumentMatchers.eq("/data/gameIdTest"),
                    ArgumentMatchers.eq(HttpMethod.POST),
                    ArgumentMatchers.any(),
                    ArgumentMatchers.<Class<JsonNode>>any()))
            .thenReturn(new ResponseEntity<>(responseNode, HttpStatus.OK));
    
        // do the call
        httpDataService.getData("gameIdTest");