I'm trying to write unit test case for HttpHandler class which has rest template call for delete. I've crated a usercontroller class to make resttemplate call in order to test the functionality of sendDelete
in HttpHandler class. Can someone help me too understand what is the correct way to write unit test case for sendDelete
method in HtttpHandler
class?
I have a class HttpHandler. It has a function sendDelete
where it calls resttemplate.exchange
method
@Service
public class HttpHandler {
public <T,R> ResponseEntity<Void> sendDelete(String url, HttpHeaders httpHeaders, R requestBody, Class<T> responseClass) {
//create an instance of rest template
RestTemplate restTemplate = new RestTemplate();
HttpEntity<R> entity = new HttpEntity<R>(requestBody, httpHeaders);
logger.info("DELETE request to " + url + " with body: " + JsonUtil.jsonizeExcludeNulls(requestBody));
//make an HTTP DELETE request with headers
ResponseEntity<Void> response = restTemplate.exchange(url, HttpMethod.DELETE, entity, Void.class);
logger.info("DELETE" + url + ": " + JsonUtil.jsonize(response));
return response;
}
}
I'm using junit5. Below is the unit test case for sendDelete method in above class:
@LocalServerPort
private int port;
private String baseUrl;
@Autowired
private HttpHandler httpHandler;
@BeforeEach
public void setBaseUrl(){
this.baseUrl = "http://localhost:"+ port + "/users";
}
@Test
public void testSuccessDeleteUserById() throws Exception{
this.baseUrl = baseUrl + "/1";
//create headers
HttpHeaders httpHeaders = new HttpHeaders();
//set content type
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
//make an HTTP DELETE request with headers
ResponseEntity<Void> actual = httpHandler.sendDelete(baseUrl, httpHeaders, null, Void.class);
assertEquals(404, actual.getStatusCodeValue());
}
Below is the user controller class
@RestController
public class UserController {
@DeleteMapping("/users/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable("userId") int userId){
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
}
}
Thank you for your time!
There are two ways to do it.
Mocking RestTemplate. To do it, first, you have to make the RestTemplate a field, and inject it through the constructor (or any other way). This allows you to inject a mock object. Then, the rest is plain and simple mocking.
You can use MockWebServer. This way you do not need to change anything. It is just a web server that your method will send the request to. After the method call finishes, you can access the recorded request and make some validations.
Here's a crude example. If you will have a lot of those tests, then you can move the web server initialization to a @BeforeEach
method and the destroying to @AfterEach
method.
public class HttpHandlerTest {
private final HttpHandler handler = new HttpHandler();
@Test
@SneakyThrows
public void testDelete() {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.start(9889);
mockWebServer.enqueue(
new MockResponse().setResponseCode(200)
);
String url = "http://localhost:9889";
Hello hello = new Hello("hello world");
final ResponseEntity<Void> entity = handler.sendDelete(url, null, hello, Hello.class);
assertNotNull(entity);
assertEquals(200, entity.getStatusCode().value());
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("DELETE", recordedRequest.getMethod());
mockWebServer.close();
}
}
// just an example class to use as a payload
class Hello {
String text;
public Hello() {
}
public Hello(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Note. Even though you will not opt for the first solution, I recommend you do abandon initializing RestTemplate for each request. You better use WebClient instead. If you do so, the first solution will not work anymore, while the second will remain intact.