1

This question has already been asked. The accepted answer doesn't work for me. Here is my code:-

My service is here:

@Service
public class PlantService {

    @Autowired
    RestTemplate restTemplate;

    static String url = "http://some_url_?Combined_Name=Oak";

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

    public String getJson() {
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        return response.getBody();
    }
}

My unit test

@RunWith(SpringRunner.class)
class PlantServiceTest {

    private PlantService plantService;
    @Mock
    @Autowired
    private RestTemplate restTemplate;

    @Before
    void setUp() {
        MockitoAnnotations.initMocks(this);
        plantService = new PlantService();
    }

    @Test
    void testGetJsonString() {
        // arrange
        String expectedJson = "Some json string";
        ResponseEntity mocResponse = mock(ResponseEntity.class);

        // act
        when(restTemplate.getForEntity("url", String.class)).thenReturn(mocResponse);
        String actualJson = plantService.getJson();
        // assert
        assertSame(expectedJson, actualJson);
    }
}

When I debug and step into the actual code. I can see restTemplate is null and throws java.lang.NullPointerException. So how do I unit test this code?

SSK
  • 3,444
  • 6
  • 32
  • 59
masiboo
  • 4,537
  • 9
  • 75
  • 136

4 Answers4

2

I have tried your code running on my machine.

Please find the test running test class

@RunWith(SpringRunner.class)
class PlantServiceTest {

    @InjectMocks
    private PlantService plantService;

    @Mock
    private RestTemplate restTemplate;

    String url = "http://some_url_?Combined_Name=Oak";

    @BeforeEach
    void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    void testGetJsonString() {
        // arrange
        String expectedJson = "Some json string";
        ResponseEntity mocResponse = new ResponseEntity("Some json string", HttpStatus.OK);

        // act
        when(restTemplate.getForEntity(url, String.class)).thenReturn(mocResponse);
        String actualJson = plantService.getJson();
        // assert
        assertSame(expectedJson, actualJson);
    }
}
SSK
  • 3,444
  • 6
  • 32
  • 59
  • Try running run as ->run configurations and select your test class as Junit 5 – SSK Jun 05 '20 at 06:43
  • When I tried your code, restTemplate is not null but the return is null. ResponseEntity response = restTemplate.getForEntity(url,String.class); response is null. So the call response.getBody() is nullpointer exception. – masiboo Jun 05 '20 at 06:43
  • You need to return correct vaule for your `when(restTemplate.getForEntity(url, String.class)).thenReturn(mocResponse);` then you will get the actuall response. – SSK Jun 05 '20 at 06:46
  • Check what values are getting for url in your service class and replace that value in test class for url – SSK Jun 05 '20 at 06:55
  • I imported all Junit 5 libraries for this test. So it must be running using Junit 5 . The actual JSON response is huge. It is not possible. But I tried some valid JSON as : String expectedJson = "{\"data\": \"aa\"}"; Still, it is null and null pointer exception. – masiboo Jun 05 '20 at 06:59
0

Your problem is the plantService = new PlantService(); You never inject into this selft created instance.

Solution 1

I usually do it like this:

@InjectMocks
private PlantService plantService = new PlantService();

@Mock
private RestTemplate restTemplate;

Remove the setup method and run with Mockito instead of the SpringRunner.

Solution 2

If you need the SpringRunner you can do the following:

@BeforeEach
void setUp() {
    MockitoAnnotations.initMocks(this);
    plantService = new PlantService();
    ReflectionTestUtils.setField(plantService, "restTemplate", restTemplate);
}

As I've worked with JUnit 5 in the last years, I'm not sure about the SpringRunner. In JUnit 5 I can use both extensions (Spring and Mockito at the same time). Maybe this also worked in JUnit 4.

TomStroemer
  • 1,390
  • 8
  • 28
  • Solution 1 didn't work. Still restTemplate remain null. Then tried Solution 2. At this point restTemplate is not null but ` ResponseEntity response = restTemplate.getForEntity(url,String.class);` retrun ull So next line response.getBody() nullpointer exception. So i tired to mock as this: ` when(plantService.getJson()).thenReturn(expectedJson); when(restTemplate.getForEntity("url",String.class)) .thenReturn(new ResponseEntity<>(expectedJson, HttpStatus.OK)); ` – masiboo Jun 03 '20 at 12:58
  • when(plantService) ... is wrong. You're not mocking plantService so you can mock the method. And that wouldn't have any use - you woud test if you mock correctly and not testing if your service does the correct thing. When using ```when(...)``` you need to use ```ArgumentMatchers``` to check your arguments. Like ```when(restTemplate.getForEntity(ArgumentMatchers.any(), ArgumentMatechers.any()).thenReturn(new ResponseEntity<>(expectedJson, HttpStatus.OK));``` (this mocks every call with every argument). Article about argument matching: https://www.baeldung.com/mockito-argument-matchers – TomStroemer Jun 03 '20 at 13:08
0

You can do the following:

  1. Remove @RunWith annotation.

  2. Annontate your test class with @RestClientTest from org.springframework.boot.test.autoconfigure.web.client.RestClientTest.

  3. Use MockRestServiceServer from org.springframework.test.web.client.MockRestServiceServer.

  4. Mock the response of the server when being called in the test method, example:
@RestClientTest
public class MyTest {

  @Autowired
  private MockRestServiceServer server;

  public void test() {

    // setup
    String expected = "test_value";
    server.expect(requestToUriTemplate("/myendpoint"))
        .andRespond(withSuccess(myJsonResponse, MediaType.APPLICATION_JSON));

    // act
    String actual = myClient.fetch(myRequestDto);

    // assert
    assertThat(actual, equalTo(expected));
    server.verify();
  }
}

I'm using assertThat from hamcrest, you can use whatever you want to assert the correctness of the result.

Ahmed El-Gamal
  • 180
  • 3
  • 18
  • I got this error "java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate" – masiboo Jun 03 '20 at 13:12
0

You should use constructor injection in your PlantService. For instance:

public class PlantService {
  RestTemplate restTemplate;

  @Autowired
  public PlantService(RestTemplate restTemplate){
    this.restTemplate = restTemplate;
  }
}

And on your test you can just do:

plantService = new PlantService(restTemplate);
                                   ^^
                                 yourMock
fjsv
  • 705
  • 10
  • 23