Home > OS >  Spring Boot Microservices - RestTemplate Null Pointer Exception in JUnit Test
Spring Boot Microservices - RestTemplate Null Pointer Exception in JUnit Test

Time:12-20

I have a problem running Junit Service in my spring-boot microservice. I defined bearer token getting from /authenticate/login. It is valid. I can send a request to any service through Postman.

I cannot run the test method after copying bearer token and defining it here as shown below.

Here is the its test method

@DisplayName("Get Order - Success Scenario")
    @Test
    void test_When_Order_Success() {

        String bearerToken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJVc2VyIiwiaXNzIjoiUk9MRV9VU0VSICIsImlhdCI6MTY3MTQ4ODgyMiwiZXhwIjoxNjcxNDg4OTQyfQ.g83kKmFzDH539ZcpxM9D8bE_famFOevkOqNst_E8YG07b4yR4cEqcrySvz36vw8GJxTKm9gUQIM1J_G8cC5RGQ";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer "   bearerToken);

        HttpEntity<String> request = new HttpEntity<String>(headers);

        //Mocking
        Order order = getMockOrder();
        when(orderRepository.findById(anyLong()))
                .thenReturn(Optional.of(order));

        when(restTemplate.exchange(
                "http://PRODUCT-SERVICE/product/"   order.getProductId(),
                HttpMethod.GET, request, ProductResponse.class).getBody()).thenReturn(getMockProductResponse());

        when(restTemplate.exchange(
                "http://PAYMENT-SERVICE/payment/order/"   order.getId(),
                HttpMethod.GET, request, PaymentResponse.class).getBody()).thenReturn(getMockPaymentResponse());

        //Actual
        OrderResponse orderResponse = orderService.getOrderDetails(1,bearerToken);

        //Verification
        verify(orderRepository, times(1)).findById(anyLong());
        verify(restTemplate, times(1)).getForObject(
                "http://PRODUCT-SERVICE/product/"   order.getProductId(),
                ProductResponse.class);
        verify(restTemplate, times(1)).getForObject(
                "http://PAYMENT-SERVICE/payment/order/"   order.getId(),
                PaymentResponse.class);

        //Assert
        assertNotNull(orderResponse);
        assertEquals(order.getId(), orderResponse.getOrderId());
    }

Here is the error shown below.

java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getBody()" because the return value of "org.springframework.web.client.RestTemplate.exchange(String, org.springframework.http.HttpMethod, org.springframework.http.HttpEntity, java.lang.Class, Object[])" is null

How can I fix the issue?

Here is the repo : Link

To run the app,

  1. Run Service Registery (Eureka Server)

  2. Run config server

  3. Run zipkin and redis through these commands shown below on docker

    docker run -d -p 9411:9411 openzipkin/zipkin docker run -d --name redis -p 6379:6379 redis

  4. Run api gateway

  5. Run other services

CodePudding user response:

when(restTemplate.exchange(
                "http://PRODUCT-SERVICE/product/"   order.getProductId(),
                HttpMethod.GET, request, ProductResponse.class).getBody()).thenReturn(getMockProductResponse());

when(restTemplate.exchange(
                "http://PAYMENT-SERVICE/payment/order/"   order.getId(),
                HttpMethod.GET, request, PaymentResponse.class).getBody()).thenReturn(getMockPaymentResponse());

You would have to first mock restTemplate.exchange(...) and return a mocked ResponseEntity. Then you can mock responseEntity.getBody().

Please note that RestTemplate is deprecated and should be replaced with WebClient: https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/index.html?org/springframework/web/client/RestTemplate.html

In our project we're using the following best practice:

Wrap the RestTemplate/ WebClient with a Client component, which does not provide any business logic, just a nice API. We're testing this class with SpringBootTest and Wiremock. In the services we then can mock this custom client, which is much easier than mocking RestTemplate/ WebClient.

CodePudding user response:

Instead of mocking the method, you can mock the response by using WireMock. This can make your test more realistic.

Also, calling RestTemplate means you are doing an integration test. This also means you are loading all your spring context and access your controller through a query you send with your RestTemplate and you don't need to mock your repository nor your service. You can only mock an external service while doing an integration or and end to end test.

  • Related