Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a RestTemplate in Spring

I am trying to test my spring rest client but I am having trouble. Here is my client class:

@Component
public class MyClient{    

    private RestTemplate restTemplate;


    @Autowired
    public MyClient(RestTemplateBuilder restTemplateBuilder,ResponseErrorHandler myResponseErrorHandler) {
        this.restTemplate = restTemplateBuilder
                .errorHandler(myResponseErrorHandler)
                .build();
    }
    //other codes here
}

here myResponseErrorHandler is class that overrides handleError and hasError methods of ResponseErrorHandler class.

Now my test class is as

    @RunWith(SpringRunner.class)
    public class MyClientTest {    

        @InjectMocks
        MyClient myClient;
        @Mock
        RestTemplate restTemplate;
        @Mock
        RestTemplateBuilder restTemplateBuilder;

       //test cases here
    }

But I am getting an error as below and I am not sure how to fix this.

You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
like image 808
user9735824 Avatar asked Feb 25 '19 22:02

user9735824


2 Answers

That problem is because when you run your app normally, Spring Boot configures RestTemplateBuilder for you automatically (because of @SpringBootApplication annotation), but in your test you don't have corresponding @SpringBootTest annotation, and restTemplateBuilder in MyClient's constructor is of course null (which produces your error while trying to call build() method on it).

If you add it as is, it would use the app default configuration context, and both RestTemplateBuilder and MyResponseErrorHandler would be working beans (Note that in that case both MyClient and MyResponseErrorHandler should've been configured as beans - f.e. by marking them with @Component).

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyClientTest {

    @Autowired
    private MyClient myClient;

    @Mock
    private MyResponseErrorHandler myResponseErrorHandler;

    @Test
    public void sampleTest() throws IOException {
        //The example of configuring myResponseErrorHandler behavior
        Mockito.when(myResponseErrorHandler.hasError(Mockito.any())).thenReturn(true);

        //Call myClient method

        //Asserts...
    }
}

Also, don't do anything with RestTemplate, because it would be created programmatically inside MyClient constructor.

Another approach is to create a separate test configuration, where you can get the default RestTemplateBuilder bean (provided by Spring) and mocked MyResponseErrorHandler (for configuring its behavior later). It could give you the full control of how to configure all of your beans for tests without using your app context.

If you want to dig into it - here are the steps to achieve this:

  1. Create test configuration class with MyResponseErrorHandler bean:
@TestConfiguration
public class MyClientTestConfiguration {

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Bean
    public ResponseErrorHandler myResponseErrorHandler() {
        return Mockito.mock(MyResponseErrorHandler.class);
    }


    @Bean
    public MyClient myClient() {
        return new MyClient(restTemplateBuilder, myResponseErrorHandler());
    }
}
  1. Your test class would be like this:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyClientTestConfiguration.class)
public class MyClientTest {

    @Autowired //wired from MyClientTestConfiguration class
    private MyClient myClient;

    @Autowired //wired from MyClientTestConfiguration class
    private MyResponseErrorHandler myResponseErrorHandler; 

    @Test
    public void sampleTest() throws IOException {
        //The example of configuring myResponseErrorHandler behavior
        Mockito.when(myResponseErrorHandler.hasError(Mockito.any())).thenReturn(true);

        //Calling myClient method...

        //Asserts...
    }
}
like image 109
amseager Avatar answered Sep 23 '22 03:09

amseager


You have a good example for mocking autowired service with dependencies,

In your case you need to mock also ResponseErrorHandler:

@RunWith(MockitoJUnitRunner.class)
public class MyClientTest {

@Mock
private ResponseErrorHandler responseErrorHandler ;

@Mock
private RestTemplateBuilder restTemplateBuilder ;

private MyClient myClient;

@Before
void setUp() {
    myClient = new MyClient(restTemplateBuilder ,responseErrorHandler );
}
like image 25
user7294900 Avatar answered Sep 22 '22 03:09

user7294900