How to Mock client side rest service - spring-boot

I'm trying to create mockito test run for rest api below is the controller class followed by mock test which I'm trying to execute but the problem is it is still calling actual rest api instead of mocking it,
1) Controller Class
public void sendData(ID id, String xmlString, Records record) throws ValidationException{
ClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
header.add("x-api-key",api_key);
header.add("Content-Type",content_type);
header.add("Cache-Control",cache_control);
HttpEntity<String> request = new HttpEntity<>(xmlString, header);
try {
restTemplate.postForEntity(getUri(id,record), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException("Error occurred while sending a file to some server "+e.getResponseBodyAsString());
}
}
2) Test class
#RunWith(MockitoJUnitRunner.class)
public class Safe2RestControllerTest {
private MockRestServiceServer server;
private RestTemplate restTemplate;
private restControllerClass serviceToTest;
#Before
public void init(){
//some code for initialization of the parameters used in controller class
this.server = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder(true).build();
}
#Test
public void testSendDataToSafe2() throws ValidationException, URISyntaxException {
//some code here when().then()
String responseBody = "{\n" +
" \"responseMessage\": \"Validation succeeded, message
accepted.\",\n" +
" \"responseCode\": \"SUCCESS\",\n" +
" 2\"responseID\": \"627ccf4dcc1a413588e5e2bae7f47e9c::0d86869e-663a-41f0-9f4c-4c7e0b278905\"\n" +
"}";
this.server.expect(MockRestRequestMatchers.requestTo(uri))
.andRespond(MockRestResponseCreators.withSuccess(responseBody,
MediaType.APPLICATION_JSON));
serviceToTest.sendData(id, xmlString, record);
this.server.verify();
}
}
How should I go ahead, any suggestion would be appreciated.

Spring's MVC test apparatus makes this quite easy.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = YourController.class)
public class YourControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testSendDataToSafe2() throws Exception {
// prepare e.g. create the requestBody
MvcResult mvcResult = mockMvc.perform(post(uri).contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().isOk())
.andReturn();
Assert.assertEquals(responseBody, mvcResult.getResponse().getContentAsString());
}
}
For more details, see the section titled "Add Unit Tests" here and/or the section titled "Auto-configured Spring MVC tests" here.
Your question also states "the problem is it is still calling actual rest api" so I'm guessing that, in addition to calling your controller is a test context, you want that mock out some of the behaviour of that controller. Specifically, you want to mock the RestTemplate instance used in that controller. If so, then would have to change the controller implementation such that the RestTemplate instance is an #Autowired class member. Then you would declare a mock for that in your test case like so:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = YourController.class)
public class YourControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private RestTemplate restTemplate;
#Test
public void testSendDataToSafe2() throws Exception {
// prepare e.g. create the requestBody
// tell your mocked RestTemplate what to do when it is invoked within the controller
Mockito.when(restTemplate.postForEntity(..., ..., ...)).thenReturn(...);
MvcResult mvcResult = mockMvc.perform(post(uri).contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().isOk())
.andReturn();
Assert.assertEquals(responseBody, mvcResult.getResponse().getContentAsString());
}
}
The above code is valid for spring-test:4.3.10.RELEASE.

Related

How to correctly mock Interceptor pre handle method in spring boot integration tests

I'm writing an integration test to an API which receives #RequestAttribute List<String> permissions and HttpServletRequest request as method parameters. There is a custom interceptor which overrides the preHandle() method of HandlerInterceptor. This method receives HttpServletRequest request and HttpServletResponse response as parameters. Based on some logic there are some attributes set in HttpServletRequest request.
I'm writing an integration test where in I send an Http request to the endpoint of the application. I want to mock the interceptor and set these attributes myself.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringRunner.class)
public class TradeChargesTest {
#LocalServerPort
public int port;
#MockBean
AppInterceptor interceptor;
// other stuff
#BeforeEach
void initTest() throws Exception {
// Want to write the mock interceptor logic here.
}
public TestRestTemplate restTemplate = new TestRestTemplate();
public String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
}
Test method:
public class UserInfoControllerTest extends TradeChargesTest {
TestRestTemplate restTemplate = new TestRestTemplate();
HttpHeaders headers = new HttpHeaders();
#Test
public void testUserInfoController(){
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity response = restTemplate.exchange(createURLWithPort("Testing/endpoint/user/v5/getUser?from_date=2022-04-01&page=1"), HttpMethod.GET, entity, Object.class);
assertEquals(200, response.getStatusCodeValue());
LinkedHashMap<String, Object> responseBodyMap = (LinkedHashMap<String, Object>) response.getBody();
assertEquals(3, responseBodyMap.get("totalHits"));
assertEquals(1, responseBodyMap.get("page"));
}
}

unable to hit on url using RestTemplate in Junit Test

I am not able to run the restTemplate whenever I run the unit test. Is there anything I need to set in the Unit Test class?
http://localhost:8082/test is another program running, whenever I run the unit test, it will get stuck at the restTemplate.postForEntity and keep rolling. Can anyone advise?
I don't wish to mock http://localhost:8082/test
Below is my source code.
UserService.java
#Service
public class UserService{
public void processUserInfo(User user){
HttpHelperService service = new HttpHelperService();
service.callOthers();
}
}
HttpHelperService.java
#Service
public class HttpHelperService{
#Autowired
RestTemplate restTemplate;
String url = "http://localhost:8082/test"
public void callOthers(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new MultiValueMap<String, Object>();
body.add("param1", "test");
body.add("param2", "test1234");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.postForEntity(url, requestEntity, String.class); //when run the unit test (AppTest), it will get stuck here.
}
}
My unit test below
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AppTest{
#Autowired
UserService userService;
#Test
public void test1(){
userService.processUserInfo();
}
}

MockMvc does not return created resource in post response

Normally when you post to a spring data rest endpoint the response contains the location header with the url to the newly created resource and the json representation of the new resource in its body.
But when I post to MockMvc like following:
#RunWith(SpringRunner.class)
#SpringBootTest
public class OrderRestTest {
#Autowired
private ObjectMapper objectMapper;
#Autowired
private WebApplicationContext context;
#Autowired
private OAuthHelper oAuthHelper;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
public void testSuperuserCanCreateOrder() throws Exception {
RequestPostProcessor accessToken = oAuthHelper.addBearerToken("someSuperUser", "ROLE_SUPERUSER");
Order order = new Order();
order.salesMemo = "some sales memo";
String responseFromTestRestTemplate = objectMapper.writeValueAsString(order);
assertNotNull(responseFromTestRestTemplate);
mockMvc.perform(
post("/ErPApI/orders")
.with(accessToken)
.content(objectMapper.writeValueAsString(order))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful());
mockMvc.perform(
get("/ErPApI/orders")
.with(accessToken))
.andExpect(jsonPath("_embedded.orders", hasSize(1)))
.andExpect(jsonPath("_embedded.orders[0].salesMemo", is("some sales memo")))
.andReturn();
}
}
the post is successful but the response body is blank. Is there a way to simulate the real response with MockMvc? Is my setup wrong?
set the Accept header to application/json too in the request.

How to mock resttemplate calls in spring boot?

I tried to write test cases for the rest calls in my service which is calling the 3rd party api.
#RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
private ForceService forceService;
#Mock
private ForceServiceConfig config;
#Mock
private RestTemplate restTemplate;
#Before
public void setup() {
forceService = new ForceService(config);
}
#Test
public void apiCall_valid() throws JSONException {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"id=null",
headers);
config.authTokenUrl = "https://ex...com/..";
Mockito.when(restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity, Access.class)).thenReturn(null);
// Mockito.when(any()).thenReturn(null);
forceService.apiCall();
}
}
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate();
public ForceService(ForceServiceConfig config) {
this.config = config;
}
private String apiCall() {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"&id=" + config.id,
headers);
ResponseEntity<Access> response = restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity,
Access.class);
return response.getBody().token_type + " " + response.getBody().access_token;
}
}
I get the following error:
org.springframework.web.client.HttpClientErrorException: 404 Not Found
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
It is calling the api in test class, which I do not want to happen.
I need to mock the resttemplate call of 3rd party api. How can I do it without being actually calling the api?
This is the problem
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate(); // HERE
you are creating new REAL rest template. What you probably want is to
Use mock that you created in wrapping test class
Use real rest template and SPY on it.
A don't know is that your actualy structure (1 file 2 classes) but it is safe to assume it is not and in any case you can simply pass RestTemplate as ctor argument. So
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate;
public ForceService(ForceServiceConfig config,RestTemplate restTemplate) {
this.restTemplate=restTemplate;
this.config = config;
}
and
#Before
public void setup() {
forceService = new ForceService(config,restTemplate);
}
Now if you want to RestTemplate to be just a stub that does literally nothing and return null on any calls if not instructed otherwiser - leave it as #Mock.
If you want however, to allow it to work normally and only intercept some specific method calls and stub responses, use spy.
#Mock
private RestTemplate restTemplate;
private RestTemplate restTemplate=Mockito.mock(RestTemplate.class)
or
private RestTemplate restTemplate=Mockito.spy(new RestTemplate());

How to Mock rest service

I'm wondering how do I mock the rest controller for the code below,
public void sendData(ID id, String xmlString, Records record) throws ValidationException{
ClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
header.add("x-api-key",api_key);
header.add("Content-Type",content_type);
header.add("Cache-Control",cache_control);
HttpEntity<String> request = new HttpEntity<>(xmlString, header);
try {
restTemplate.postForEntity(getUri(id,record), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException("Error occurred while sending a file to some server "+e.getResponseBodyAsString());
}
}
Any suggestion would be helpful.
I tried to do something like this,
#RunWith(MockitoJUnitRunner.class)
public class Safe2RestControllerTest {
private MockRestServiceServer server;
private RestTemplate restTemplate;
private restControllerClass serviceToTest;
#Before
public void init(){
//some code for initialization of the parameters used in controller class
this.server = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder(true).build();
}
#Test
public void testSendDataToSafe2() throws ValidationException, URISyntaxException {
//some code here when().then()
String responseBody = "{\n" +
" \"responseMessage\": \"Validation succeeded, message
accepted.\",\n" +
" \"responseCode\": \"SUCCESS\",\n" +
" 2\"responseID\": \"627ccf4dcc1a413588e5e2bae7f47e9c::0d86869e-663a-41f0-9f4c-4c7e0b278905\"\n" +
"}";
this.server.expect(MockRestRequestMatchers.requestTo(uri))
.andRespond(MockRestResponseCreators.withSuccess(responseBody,
MediaType.APPLICATION_JSON));
serviceToTest.sendDataToSafe2(id, xmlString, record);
this.server.verify();
}
}
This is the test case what I'm trying to do but it still calling actual rest api
As pointed out by #JBNizet, you should take a look at MockRestServiceServer. It allows you to test Spring components which are using RestTemplate to make HTTP calls.
See MockRestServiceServer and #RestClientTest.

Resources