How to Send an Empty Body POST Request using MockMvc - spring-boot

I have a simple Controller class that I am attempting to test using MockMvc. At the moment, the controller class is just an endpoint that is intended to accept an empty POST body and return a 200 response.
Controller:
#RestController
#RequestMapping("/api/v1/transactions")
public class TransactionController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public Transaction createTransaction(#RequestBody final Transaction transaction) {
return transaction;
}
}
Test:
class TransactionControllerTest {
private static final String URL_TEMPLATE = "/api/v1/transactions";
#Autowired
private MockMvc mockMvc;
#Test
void shouldReturn200() throws Exception {
final String emptyBody = "{}";
mockMvc.perform(post(URL_TEMPLATE)
.contentType(MediaType.APPLICATION_JSON)
.content(emptyBody))
.andExpect(status().isOk());
At the moment, the test is returning an NPE. However, when I run the application locally and submit a POST request via postman with headers Content-Type = application/json & Accept = application/json, with a raw body of {}, I get a successful 200 response.
Am I doing something incorrectly with the empty string and mockMvc?

Related

Unit tests for testing methods returning CompletableFuture always returns NullPointerException

I have a SpringBoot Component and few methods. It returns CompletableFuture and I want to cover those lines with Unit Tests. But it always ends up in NullPointer Exception.
#Component
Class AsyncClass {
// My implementation of RESTClient
#Autowired
RestClient httpClient;
#Async
public CompletableFuture<TestClass> getDetails() {
// Retrieves the headers
HttpHeaders headers = getHttpHeaders();
HttpEntity<String> request = new HttpEntity<>(headers);
InitiativeResponse initiativeResponse = httpClient.executeRequest(API_URL, HttpMethod.GET, request, Object.class).getBody();
return CompletableFuture.completedFuture(TestClass);
}
private HttpHeaders getHttpHeaders(String userId) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONSUMER_ID, consumerId);
headers.add(USER_ID, userId);
return headers;
}
}
This works perfectly fine and I autowire this into my service class and get the results. The problem arises when I write unit test cases for this class.
Class TestAsyncClass {
#InjectMocks
AsyncClass asyncClass;
#BeforeEach
void setUp() {
openMocks(this);
}
#Test
void testGetDetails() {
asyncClass.getDetails();
}
}
The above unit test fails with NullPointer Exception. Can you please help me on what am I missing?
The stacktrace simple shows
ava.lang.NullPointerException
at com.example.services.helper.TestAsyncClass.testGetDetails(TestAsyncClass.java:11)
I am not very sure but do you have #RunWith(MockitoJUnitRunner.class) or MockitoAnnotations.initMocks(this) in your TestAsyncClass ? I have the gut feelings that the mocks are not initiated....

How to unit test a multipart POST request with Spring MVC Test?

I am trying to create unit test for REST APi but having big trouble with the uploading excel method.
Here is the method on the controller side
#RestController()
#RequestMapping(path = "/upload")
#CrossOrigin(origins = "http://localhost:4200")
public class FileController {
#Autowired
FileService fileService;
#PostMapping(value = "/{managerId}/project/{projectId}")
public List<Task> importExcelFile(#RequestParam("file") MultipartFile files, #PathVariable int managerId,
#PathVariable int projectId) throws IOException, ParseException {
return fileService.getTasksFromExcel(files, managerId, projectId);
}
Whatever I try I get a lot of errors and evidently I don't really understand what I am supposed to do.
The main error I get is
current request is not a multipart request
You can do the following.
I just simplified your example a tiny bit.
So, here's the controller that returns the file size of the file it receives.
#RestController
#RequestMapping(path = "/upload")
public class FileController {
#PostMapping(value = "/file")
public ResponseEntity<Object> importExcelFile(#RequestParam("file") MultipartFile files) {
return ResponseEntity.ok(files.getSize());
}
}
and this one is the test of it. There is a class called MockMvc that Spring provides to easily unit test your controllers and controller advices. There is a method called multipart that you can use to simulate file upload cases.
class FileControllerTest {
private final MockMvc mockMvc = MockMvcBuilders
.standaloneSetup(new FileController())
.build();
#Test
#SneakyThrows
void importExcelFile() {
final byte[] bytes = Files.readAllBytes(Paths.get("TEST_FILE_URL_HERE"));
mockMvc.perform(multipart("/upload/file")
.file("file", bytes))
.andExpect(status().isOk())
.andExpect(content().string("2037")); // size of the test input file
}
}
Generally Multipart uploads can be tested via MockMultipartFile:
https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/file-upload-test.html

Spring boot MockMvc post not enter to real conntroller method

I have controllers method
#PostMapping(value = "/getTransaction/{transactionUuid}")
public ResponseEntity<TransactionDetail> getTransaction(#PathVariable() String transactionUuid) {
return ResponseEntity.ok(transactionsService.getOpcTransaction(transactionUuid));
}
I write test for controller:
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
mockMvc.perform(post("/nfp-server/getTransaction/{transactionUuid}", "123"))
.andDo(print());
}
I star test in debug mode - but I not enter to controller getTransaction method. And in log I see:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /server/getTransaction/123
Parameters = {}
Headers = {}
In your controller your mapping is
/getTransaction/{transactionUuid}
In your test class you are passing
/nfp-server/getTransaction/{transactionUuid}
There is a mismatch.

How to add CORS headers to the Spring error page rendered by BasicErrorController?

I have a single page client being served by a Spring Boot REST MVC API application (spring boot version 1.5.2).
My app is secured via Auth0 JWT tokens. When things are working, the CORS headers for responses are provided by a ServletFilter that gets configured as part of setting up the security:
protected void configure(HttpSecurity http) throws Exception {
...
http.addFilterBefore(simpleCORSFilter(), Auth0AuthenticationFilter.class);
...
}
This seems to work everywhere I've tested it so far - but one place where it's not working is with the default Spring error page (path "/error", rendered by default by the BasicErrorController class).
When there's an exception thrown in my service methods, the error page works and renders the content I want as JSON in the response body, but the client app can't access the http response body because the response lacks CORS headers.
So the question: "how do I add CORS headers to the error page"?
Should I be removing the CORS filter from my security setup and applying the CORS filter more globally? Where would that be done - I can't find anything relevant in the Spring doccumentation.
Or should I be writing a custom Error controller? The only example of a custom error controller in the documentation just seems to allow you to return a string.
You can define a separate Controller for Error and allow cross origin to it using
#CrossOrigin("*")
Combining Poorvi's answer with Joni Karppinen's custom error controller code gives:
#RestController
public class ErrorController
implements org.springframework.boot.autoconfigure.web.ErrorController
{
private static final String PATH = "/error";
#Autowired private ErrorAttributes errorAttributes;
#Override
public String getErrorPath(){
return PATH;
}
// I guess when time comes to lock down cors header, we could use a spring
// value configuration here to share with corsfilter.
#CrossOrigin("*")
#RequestMapping(value = PATH, produces = "application/json")
public #ResponseBody
ErrorJson error(HttpServletRequest request, HttpServletResponse response){
return new ErrorJson(
response.getStatus(),
getErrorAttributes(request, false) );
}
private Map<String, Object> getErrorAttributes(
HttpServletRequest request,
boolean includeStackTrace
){
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
return errorAttributes.getErrorAttributes(
requestAttributes,
includeStackTrace);
}
}
class ErrorJson {
public Integer status;
public String error;
public String message;
public String timeStamp;
public String trace;
public ErrorJson(int status, Map<String, Object> errorAttributes){
this.status = status;
this.error = (String) errorAttributes.get("error");
this.message = (String) errorAttributes.get("message");
this.timeStamp = errorAttributes.get("timestamp").toString();
this.trace = (String) errorAttributes.get("trace");
}
}
Which seems to do the job for me.

How to send HTTP OPTIONS request with body using Spring rest template?

I am trying to call a RESTfull web service resource, this resource is provided by a third party, the resource is exposed with OPTIONS http verb.
To integrate with the service, I should send a request with a specific body, which identities by a provider, but when I did that I got a bad request. After that I trace my code then I recognized that the body of the request is ignored by rest template based on the below code:
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
my question, is there a standard way to override this behavior or I should use another tool?
The code you've pasted is from
SimpleClientHttpRequestFactory.prepareConnection(HttpURLConnection connection, String httpMethod)
I know because I've debugged that code few hours ago.
I had to do a HTTP GET with body using restTemplate. So I've extend SimpleClientHttpRequestFactory, override prepareConnection and create a new RestTemplate using the new factory.
public class SimpleClientHttpRequestWithGetBodyFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
Create a new RestTemplate based on this factory
new RestTemplate(new SimpleClientHttpRequestWithGetBodyFactory());
A test to prove the solution is working using spring boot (#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT))
public class TestRestTemplateTests extends AbstractIntegrationTests {
#Test
public void testMethod() {
RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestWithBodyForGetFactory());
HttpEntity<String> requestEntity = new HttpEntity<>("expected body");
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:18181/test", HttpMethod.GET, requestEntity, String.class);
assertThat(responseEntity.getBody()).isEqualTo(requestEntity.getBody());
}
#Controller("/test")
static class TestController {
#RequestMapping
public #ResponseBody String testMethod(HttpServletRequest request) throws IOException {
return request.getReader().readLine();
}
}
}

Resources