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
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:
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());
}
}
@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...
}
}
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 );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With