Content Type Not Set Junit REST POST test - rest

I have a Spring Boot REST application. The unit tests for all of the GET requests are working perfectly; however, the POST requests are all returning
java.lang.AssertionError: Content type not set
Here is the controller:
#RestController
public class ClassificationController {
private IClassificationService classificationService;
#Autowired
public ClassificationController(IClassificationService classificationService) {
this.classificationService = classificationService;
}
#RequestMapping(value="/category", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public CategoryDTO createCategory(#RequestBody final CategoryDTO category) throws MctException {
return classificationService.createCategory(category);
}
The unit test I have is:
#RunWith(MockitoJUnitRunner.class)
public class ClassificationControllerTest {
#Mock
private IClassificationService classificationService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ClassificationController(classificationService)).build();
}
#Test
public void createCategoryTest() throws Exception {
String jsonTask = String.format("{\"id\": \"2\",\"categoryName\": \"Category Name 2\"}");
MvcResult result = mockMvc.perform(post("/category")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonTask))
.andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":2")))
.andExpect(content().string(containsString("\"categoryName\":\"Category Name 2\"")))
.andExpect(status().isCreated())
.andReturn();
}
I have also tried this with a CategoryDTO object instead of the String jsonTask with the same result.

I discovered it was just failing on that assertion because it was the first one, but it just wasn't returning anything from the endpoint. I am returning the content type because it is returning the object that is being inserted so a content type is valid. I ended up changing my test create the content JSON using an ObjectMapper and then I had to add an equals method on my domain object....once I added the equals method, the test passed. I didn't realize the mock framework used that method.
#Test
public void createClassTest() throws Exception {
String jsonInString = objectMapper.writeValueAsString(singleClass);
when(classificationService.createClass(5, singleClass)).thenReturn(singleClass);
MvcResult result = mockMvc.perform(post("/class/5")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonInString))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":1")))
.andExpect(content().string(containsString("\"className\":\"Test Class Name 1\"")))
.andExpect(status().isCreated())
.andReturn();
verify(classificationService).createClass(5, singleClass);
}

Judging by the assertion error, it seems the endpoint is not returning a MediaType.APPLICATION_JSON_UTF8. Try removing the contentType check or debugging and seeing what the endpoint is actually returning. Once again, judging by the error you are seeing, seems it's not returning any content type at all. So you should probably be checking that no content type is set.
I know typically the POST request I usually test do not return a contentType at all.
After all, it could be that endpoint is actually doing something incorrectly if you do expect the content type to be set.

Related

JUnit5 - Rest API controller having custom spring validator is failing

I have a controller that accepts path parameter called 'jobName'. The #ValidateJobName is the custom validator that validates the user input. If the input is wrong then it throws the error below
"Invalid Job name, valid job names are: vendor, service, product,
pricing, currency, contract"
The issue I am facing is that, when I am testing my rest controller API the test case always fails by returning the above error even when the job name is one of the acceptable values but when I remove #ValidateJobName custom annotation from the controller my test cases gets passed.
The #ValidateJobName and Controller works all good when triggered from Postman client but when I do unit testing the test case fails.
I have tried lot of blogs and googled but could not get a solution, Below are my Controller and JUnit testcase.
Please help!
JobController.java
#Validated
#Slf4j
#RestController
public class JobController {
#Autowired
ReportService reportService;
#Autowired
ReportConfig reportConfig;
#RequestMapping(value = "/importjob/{jobName}", method = RequestMethod.GET)
ResponseEntity<DataIntegrationResponse> getReport(#PathVariable #ValidateJobName String jobName) throws Exception {
log.info("Received a request to launch the " + jobName + " Job");
return reportService.getReport(jobName);
}
}
JobControllerTest.java
#ExtendWith(MockitoExtension.class)
#WebMvcTest(JobController.class)
#AutoConfigureMockMvc
public class JobControllerTest {
#MockBean
ReportService reportService;
#MockBean
ReportConfig rep;
#MockBean
JobMapping jmap;
#Autowired
public MockMvc mockMvc;
#Test
public void testGetReport() throws Exception {
String jobNameInput="vendor";
HttpStatus httpStatus = HttpStatus.OK;
String fitsReportName = "idex_fits_vendor.csv";
String jobName = "WFitsVendorJob";
String jobStatus = "STARTED";
Long jobInstanceId = 1022L;
String message = "WFitsVendorJob triggered successfully.";
DataIntegrationResponse response = new DataIntegrationResponse(LocalDateTime.now(), httpStatus, fitsReportName, jobName, jobStatus, jobInstanceId, message);
ResponseEntity<DataIntegrationResponse> responseEntity = new ResponseEntity<DataIntegrationResponse>(response, HttpStatus.OK);
Mockito.when(reportService.getReport(jobNameInput)).thenReturn(responseEntity);
mockMvc.perform(get("/importjob/{jobName}", "vendor")).andExpect(status().isOk());
}
JobNameValidator.java
#Component
public class JobNameValidator implements ConstraintValidator<ValidateJobName, String>{
#Autowired
private JobMapping jobMap;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value!=null && !jobMap.getMappings().containsKey(value)) { return false; }
return true;
}
}
ValidateJobName.java - interface
#Documented
#Constraint(validatedBy = JobNameValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
public #interface ValidateJobName {
String message() default "Invalid Job name, valid job names are: vendor, service, product, pricing, currency, contract";
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default {};
}
This is because you use a mock of JobMapping
#MockBean
JobMapping jmap;
Your JobNameValidator receive a Mock and doesn't know what to return when calling the containsKey method.
First solution is to tell what to do with this mock :
Mockito.when(jobMapping.getMappings()).thenReturn(// Map containing "vendor");
The second solution is to import your real JobMapping class instead of a mock:
#ExtendWith(MockitoExtension.class)
#WebMvcTest(JobController.class)
#AutoConfigureMockMvc
#Import(JobMapping.class)
class JobControllerTest {
// #MockBean
// JobMapping jmap;
}

Microprofile java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream incompatible

I'm using microprofile 3.2 to call out to a rest webservice that returns a java bean in a Response entity. When I try to extract the bean from the response though I get a
java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream incompatible with <class>
error.
E.g.
My bean:
public class MyBean {
private int id;
public int getId() { return id; }
public void setId(final int id) { this.id = id; }
}
REST WS api interface:
#GET
#Path("/{id}")
Response getBean(#PathParam("id") Integer id);
REST implementation class:
public Response getBean(final Integer id) {
MyBean myBean = new Service().getBean(id);
return Response.ok(myBean).build();
}
RestClient:
IBeanResource beanResource =
RestClientBuilder.newBuilder().baseUri(apiURI).build(IBeanResource.class);
Response beanResponse = beanResource.getBean(100);
if (beanResponse.getStatus() == Response.Status.OK.getStatusCode()) {
MyBean bean = (MyBean) beanResponse.getEntity();
}
Error fires on line
MyBean bean = (MyBean) beanResponse.getEntity();
Has anyone seen this before? The documentation isn't great.
Yes that would be the expected behavior. If you inspect the value of beanResponse in debug you will see the Response is of type InboundJaxrsResonse and entity is nothing but of type HttpUrlConnector. Which is why when you try to cast it to your custom bean class it throws ClassCastException. You can try any of the following approach:
you could instead do as below
String jsonString = beanResponse.readEntity(String.class);
The above will give you the JSON response as String and then you may convert it to your respective class using libraries such as gson or jackson of your choice.
In your REST WS api interface instead of returning Response return your model MyBean. As per the Microprofile rest Client spec it states the implementation of Microprofile Rest client must provide a built-in JSON-P entity provider and if it supports JSON-B then it must provide JSON-B entity provider.
microprofile-rest-client-spec-2.0
Thanks for the reply. I'll take a look at the spec again for returning models. I like the idea of capturing the response rather than the model so I have any header or status information too e.g. how would a 404 be handed if the resource could not be found?
I was able to get around this by reading the InputStream and using jsonb to bind to the bean
InputStream is = (InputStream) beanResponse.getEntity();
return jsonb.fromJson(is, MyBean.class);

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?
For e.g. I have a query like
http://localhost:8080/all?sort=firstName,asc&sort=nosuchfield,asc
How can I handle or remove the irrelevant field "nosuchfield"?
Also, how can I limit sort parameters in URL?
If the sorting field doesn't present in the database then below exception will be thrown by Spring JPA.
org.springframework.data.mapping.PropertyReferenceException: No property nosuchfield found for type <TYPE>!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
However, the exception can be handled using various types. Ultimately, you can just log it or transform it into any custom exception. As per my requirement, I have transformed it into a custom exception.
Using AOP
#Aspect
#Component
public class UnKnownColumnSortingExceptionHandler {
#AfterThrowing(pointcut = "execution(* com.repositorypackage.*.*(..))", throwing = "exception")
public void executeWhenExceptionThrowninRepository(JoinPoint jp, Throwable ex) {
if (ex instanceof PropertyReferenceException) {
throw new CustomException("Invalid Database operation");
}
}
}
Using #ControllerAdvice(Exception handling in Application wise)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public GlobalExceptionHandler() {}
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Exception handling in Controller wise
Add the below piece of code to your controller
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

spring-boot test: #get request returns with body null

while doing Content-negotiation testing mock GET returns with null in response body although response status is 200.
java.lang.AssertionError: Response header 'Content-Type'
Expected :application/json;charset=UTF-8
Actual :null
here is full test class code. I want to verify that content type is json.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
Controller controller;
#Test
public void test() throws Exception {
mockMvc.perform(get("/query?mediaType=json"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE));
}}
here is my controller's endpoint.
#RestController
public class Controller {
#RequestMapping(value = "/query", produces = {"application/json", "application/xml"}, method = RequestMethod.GET)
public #ResponseBody ResultSet getResults(
final HttpServletRequest request
) throws Throwable {
// logic ...
SearchService search = (SearchService) context.getBean("search");
ResultSet result = search.getResults();
return result;
}
Any thoughts why Body would return as null?
The issue is with your Controller definition in your Test class. As you are testing your Controller, you should be using an actual instance of it. Get you mockMvc instance for this Controller as below (you can do it in your #Before annotated setup method):
mockMvc = MockMvcBuilders.standaloneSetup(new Controller()).build();

Spring multiple #ModelAttribute methods in the same #Controller

I have a page with two different forms (with two different submits) on Spring MVC 3, and I have a problem with #ModelAttribute methods. When I have two on the same controller, they are not always executed making the model to be NULL.
The code:
#Controller
#RequestMapping(value = "/session/admin/permission/{userId}")
public class PermissionController {
#Autowired
private UserManager userManager;
#ModelAttribute("passwordValidation")
private PasswordValidation getPasswordModel(){
return new PasswordValidation();
}
#ModelAttribute("user")
private User getUserModel(#PathVariable("userId") String userId){
//This is not executed
return userManager.getUser(userId);
}
#ModelAttribute("permissionsAvailable")
private PermissionsAvailable getPermissionsModel(#ModelAttribute("user") User user) {
return new PermissionsAvailable();
}
#RequestMapping(method = RequestMethod.GET)
public String adminPermission(){
return "/security/permission";
}
#RequestMapping(method = RequestMethod.POST, params="changeRoles")
public String modifyPermission(#ModelAttribute("permissionsAvailable") PermissionsAvailable permissions,
HttpServletRequest request, #ModelAttribute("user") User user,
final RedirectAttributes redirectAttributes){
//Modify something
}
#RequestMapping(method = RequestMethod.POST, params="changePassword")
public String modifyPassword(
#ModelAttribute("passwordValidation") PasswordValidation passwordValidation,
#ModelAttribute("user") User user,
HttpServletRequest request, BindingResult bindingResult,
final RedirectAttributes redirectAttributes){
return "newpage";
}
}
Don't know why, sometimes everything goes ok and every method is executed, but sometimes they are not executed.
UPDATE: I have two different controllers with the same problem so it must be an error on Spring or something I'm doing wrong.
Thanks.
The documentation doesn't mention anywhere that it's possible to use #ModelAttribute on an argument to a #ModelAttribute annotated method, like you're doing in your "getPermissionsModel()" method. It's possible that's not supported, since it's not documented as being supported. You might want to try either removing the "#ModelAttribute("user") User user" argument from your "getPermissionsModel()" method, and/or instead try just using one #ModelAttribute method to set all your model attributes:
#ModelAttribute
public void setAttributes(#PathVariable("userId") String userId, Model model) {
model.addAttribute(new PasswordValidation());
model.addAttribute(userManager.getUser(userId));
model.addAttribute(new PermissionsAvailable());
}