spring cloud gateway predicates test cases - java

I have implemented the predicate factory(spring cloud gateway) to validate the headers and I want to add the test cases for that
public Predicate<ServerWebExchange> apply(Config config ) {
return (ServerWebExchange t) -> {
List<String> Header = t.getRequest().getHeaders().get("abcd");
#business logic
return true;
};
}
I want to include the test cases for the predicate factory above.
I tried the test case as below
#Before
public void prepareStubs() {
stubFor(any(urlPathEqualTo("/abcd")).willReturn(aResponse().withBody("ABCD")));
}
#Test
public void testGatewayRouting() throws JsonMappingException, JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer eyJraWQiOiIiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9");
HttpEntity<?> entity = new HttpEntity<>(headers);
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(createURLWithPort("/abcd"));
ResponseEntity<String> response = restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET, entity,
String.class);
assertEquals(200, response.getStatusCodeValue());
assertEquals("ABCD", response.getBody());
}

Related

Mockito Restemplate Verify not invoked

I a trying to mock restetmplate but don't why I am getting the below error. I am using doreturn to verify the invocation of the resttemplate.Is there by other way to mock the rest template and mock the HTTP request
class ToTest{
public ResponseEntity<String> getValue(){
if (StringUtils.isNotEmpty(token)) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
headers.setContentType(MediaType.APPLICATION_JSON);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(etsServiceResourceDetails.getAccessTokenUri())
.queryParam("test","tsetValue");
builder.queryParam("some","someValue");
HttpEntity<?> entity = new HttpEntity<>(headers);
try {
response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity, String.class);
}
catch (RestClientResponseException ex ) {
log.error(ex);
if ( ex.getRawStatusCode() == HttpStatus.NOT_FOUND.value()) {
return ResponseEntity.notFound().build();
}
}
}
}
}
Unit Test for the rest Temaplate
#Test
public void testExchange() {
String token="abbjabxkcbkscksckbckbkcbksckckkcb";
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("query", "star");
queryParams.add("test", "star2");
UriComponents uriComponents = UriComponentsBuilder.newInstance().scheme("http").host("localhost:8080/some/add").path("/").queryParams(queryParams).buildAndExpand();
RestTemplate r = new RestTemplate();
RestTemplate r1 = spy(r);
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<?> entity = new HttpEntity<>(headers);
// ResponseEntity responserm=
//r1.exchange(uriComponents.toUriString(),HttpMethod.GET,entity,String.class); //I am getting error org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080
doReturn(new ResponseEntity("", HttpStatus.OK)).when(r1).exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), any(Class.class));
verify(r1, times(1)).exchange(eq(uriComponents.toUriString()), any(), any(), eq(String.class));
assertEquals("check", "http://localhost:8080/some/add/?query=s1&test=s2", uriComponents.toUriString());
}
Errors
-> at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:581)
Actually, there were zero interactions with this mock.
Wanted but not invoked:

How to test restclient using RestTemplate and JUnit?

I am new to JUNIT and using RestTemplate to call my service, I'm getting 200 response for the same. But, I can't test the class using JUnit. Tried different approaches and getting 400 and 404. I want to post the request body (json) and test the status. Please let me know if there is any issue.
/**
* Rest client implementation
**/
public class CreateEmailDelegate implements CDM {
#Autowired
private RestTemplate restTemplate;
private String url = "http://example.com/communications/emails";
public ResponseEntity<CDResponse> createEmail(CDMEmailRequest cDRequest) throws UnavailableServiceException, InvalidInputException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity< CDMEmailRequest > cDRequestEntity = new HttpEntity<>( cDRequest, httpHeaders);
ResponseEntity< CDResponse > cDResponse = null;
try {
cDResponse = restTemplate.postForEntity(url, cDRequestEntity, CDResponse.class);
} catch (Exception e) {
LOGGER.error(e.getMessage());
throw e;
}
return cDResponse;
}
}
My Test class which return 404 status instead of 200
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
CDMEmailRequest anObject = new CDMEmailRequest();
ResultMatcher ok = MockMvcResultMatchers.status().isOk();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String requestJson = ow.writeValueAsString(anObject);
System.out.println(requestJson);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(url).contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson);
this.mockMvc.perform(builder).andExpect(ok).andDo(MockMvcResultHandlers.print());
}
}
My Test class using TestRestTemplate instead MockMvc returns 400
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
// rest headers as above
}
#Test
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
String username = "";
String password = "";
HttpEntity<CDMEmailRequest>
cDEntity = new HttpEntity<>(httpHeaders);
restTemplate = new TestRestTemplate(username, password);
responseEntity =
restTemplate.exchange(url, HttpMethod.POST, cDEntity,
CDResponse.class);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
}
}
I think you're trying to implement an integration test instead of an unit test, there is quite difference. MockMvc should be used to implement unit tests and TestRestTemplate for integration tests. You can't neither use it for testing a Client implementation.
See Unit and Integration Tests in Spring Boot
If you are working with Spring Boot you could achieve your goal using another approach see this question Spring boot testing of a rest client using #RestClientTest.

JUnit test for void method containing a RestTemplate exchange call

I am trying to write a Test class for one of my methods and I am new to JUnit. My class returns a void and has a RestTemplate.exchange call to an external endpoint.
I started off trying this, but this gives me a NullInsteadOfMockException.
#Test
public void service1test() throws IOException{
ResponseEntity<?> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
Mockito.verify(restTemplate.exchange(Mockito.anyString(), Mockito.<HttpMethod> any(), Mockito.<HttpEntity<?>> any(), Mockito.<Class<?>> any(),
Mockito.<String, String> anyMap()));
}
Here's my method that I want to write unit test for.
#Autowired
private RestTemplate restTemplate;
public void service1(String a, String b) {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
this.restTemplate = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
try {
headers.set("ID", ID);
headers.set("secret", secret);
System.out.println(docStoreUrl + itemID);
HttpEntity requestEntity = new HttpEntity<>(null, headers);
ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Object.class);
log.info("Status code for get : {}", response.getStatusCodeValue());
if(response.getStatusCodeValue() == 200) {
Object document = (Object) response.getBody();
Class2.service2.execute(document);
}
else {
log.info("Failed to retrieve document due to {}", response.getStatusCodeValue());
}
}
I am new to JUnit testing and can't figure out how to write the Test class for this covering all the lines.
If this question is already answered, please point me to the appropriate URL.
Any help will be greatly appreciated.
Its a little bit late, but if anyone else falls over this:
This error will also occur, if you are using mockito wrong.
It should be:
Mockito.verify(restTemplate).exchange(Mockito.anyString() ...
Notice the closing bracket immediately after restTemplate, so exchange is called on top of mockito.

How to handle HttpClientException properly

I have a webservice which gets data from other webservice and return back to the browser.
I want to hide internal client errors
Want to throw 404, 400 etc which
are returned from the webservice in the below method.
How to resolve this problem in a neat way?
Option 1 or Option 2 is clean way?
Option 1
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
} catch (HttpClientErrorException e) {
LOG.error("Client Exception ", e);
throw new HttpClientError("Client Exception: "+e.getStatusCode());
}
return Optional.empty();
}
(or)
Option 2
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
throw new RestClientResponseException("", 400, "", null, null, null);
} catch (HttpStatusCodeException e) {
LOG.error("HttpStatusCodeException ", e);
throw new RestClientResponseException(e.getMessage(), e.getStatusCode().value(), e.getStatusText(), e.getResponseHeaders(), e.getResponseBodyAsByteArray(), Charset.defaultCharset());
}
return Optional.empty();
}
I have written a sample ResponseErrorHandler for you,
public class RestTemplateClientErrorHandler implements ResponseErrorHandler {
private static final Logger logger = LoggerFactory.getLogger(RestTemplateClientErrorHandler.class);
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return RestUtil.isError(clientHttpResponse.getStatusCode());
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
String responseBody = "";
if(clientHttpResponse != null && clientHttpResponse.getBody() != null){
responseBody = IOUtils.toString(clientHttpResponse.getBody());
}
switch(clientHttpResponse.getRawStatusCode()){
case 404:
logger.error("Entity not found. Message: {}. Status: {} ",responseBody,clientHttpResponse.getStatusCode());
throw new RestClientResponseException(responseBody);
case 400:
logger.error("Bad request for entity. Message: {}. Status: {}",responseBody, clientHttpResponse.getStatusCode());
throw new RestClientResponseException(StringUtils.EMPTY, 400,StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY);
default:
logger.error("Unexpected HTTP status: {} received when trying to delete entity in device repository.", clientHttpResponse.getStatusCode());
throw new RestClientResponseException(responseBody);
}
}
public static class RestUtil {
private RestUtil() {
throw new IllegalAccessError("Utility class");
}
public static boolean isError(HttpStatus status) {
HttpStatus.Series series = status.series();
return HttpStatus.Series.CLIENT_ERROR.equals(series)
|| HttpStatus.Series.SERVER_ERROR.equals(series);
}
}
}
Note : This is common ResponseErrorHandler for your restTemplate and it will catch all the exceptions thrown by restTemplate you don't require try,catch block in each method and you don't need to catch "HttpStatusCodeException" or any other exception.
Please use the below code to register this ErrorHandler.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestTemplateClientErrorHandler());
You can also find examples here.
You can refactor your client class like this,
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
return Optional.empty();
}
So your method not looking beautiful now ? Suggestions welcome.

Integration test with TestRestTemplate for Multipart POST request returns 400

I know that similar question has been here already couple of times but following suggested fixes did not solve my problem.
I have a simple controller with the following endpoint:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> singleFileUpload(#RequestParam("file") MultipartFile file) {
log.debug("Upload controller - POST: {}", file.getOriginalFilename());
// do something
}
I am trying to write an integration test for it using Spring TestRestTemplate but all of my attemps end with 400 - Bad Request (no logs clarifying what went wrong in console).
The log inside the controller did not get hit so it failed before getting there.
Could you please take a look on my test and suggest what am I doing wrong?
#Test
public void testUpload() {
// simulate multipartfile upload
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("image.jpg").getFile());
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", file);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(parameters, headers);
ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");
// Expect Ok
assertThat(response.getStatusCode(), is(HttpStatus.OK));
}
I tried the following:
#Test
public void testUpload() {
LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new org.springframework.core.io.ClassPathResource("image.jpg"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<LinkedMultiValueMap<String, Object>>(parameters, headers);
ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");
// Expect Ok
assertThat(response.getStatusCode(), is(HttpStatus.OK));
}
As you can see I used the org.springframework.core.io.ClassPathResource as object for the file and ti worked like a charm
I hope it's useful
Angelo
FileSystemResource also could be used in case if you want to use java.nio.file.Path.
Package: org.springframework.core.io.FileSystemResource
For example, you could do this:
new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"))
Full code example:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UploadFilesTest {
private final TestRestTemplate template;
#Autowired
public UploadFilesTest(TestRestTemplate template) {
this.template = template;
}
#Test
public void uploadFileTest() {
var multipart = new LinkedMultiValueMap<>();
multipart.add("file", file());
final ResponseEntity<String> post = template.postForEntity("/upload", new HttpEntity<>(multipart, headers()), String.class);
assertEquals(HttpStatus.OK, post.getStatusCode());
}
private HttpHeaders headers() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
return headers;
}
private FileSystemResource file() {
return new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"));
}
}
Rest controller:
#RestController
public class UploadEndpoint {
#PostMapping("/upload")
public void uploadFile(#RequestParam("file") MultipartFile file) {
System.out.println(file.getSize());
}
}

Categories