How do I get the return value from this test? My controller returns #Responsebody Meal
#Test
// #Transactional
public void testPost() {
Profile profile = ProfileUtil.getProfile();
profileService.save(profile);
String requestUri = "/user/" + profile.getId() + "/meals";
request.setMethod("POST");
request.setRequestURI(requestUri);
Object handler;
try {
handler = handlerMapping.getHandler(request).getHandler();
handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
//I would like to have the controller return value here! :)
} catch (Exception e) {
}
}
You have two choices:
call the controller directly instead of going through the handler mapping/servlet layer. This way you'll simply get the Meal object and you can run assertions on it
parse the response from response object. In your case Meal object was taken by Spring MVC and marshalled, probably to XML or JSON. The original object is now lost, you can only retrieve the XML/JSON string, parse it and run assertions on it (e.g. using XPath).
Both approaches have their advantages. Prefer the first one if you want to test the controller itself. The latter is more complex but lets you test marshalling.
Related
I'm sending AJAX request to make simple CRUD happens on my project, asynchronously
I don't need any response from server, I just need to insert, update or delete data on database.
I also learned there must be a response to a request, so I tried some ways to fake it, but all of them didn't feel right.
return meaningless value
Just return any value like boolean or empty String, and not using it
#PostMapping("/whatever")
public #ResponseBody boolean something() {
doSomething();
return true;
}
return something, but not using it
Almost same as 1, since I'm not using it
#PostMapping("/whatever")
public #ResponseBody MyObject something() {
doSomething();
return new MyObject();
}
doesn't care about response, just get errors and don't mind it
Use catch or always with ajax request, a lot of errors on console, it really doesn't feel right
#PostMapping("/whatever")
public void something() {
doSomething();
}
#PostMapping("/whatever")
public String something() {
doSomething();
return "/page/url";
}
All of them works as I want, but it makes me thinking there could be a better way to write a code when I don't need any response.
Any advice on this?
Add the #ResponseStatus(HttpStatus.NO_CONTENT) annotation to the controller route and then you do not need to return anything. Your prototype will be "public void doSomething()".
We use this for delete calls.
I'm working on an existing codebase and I'm seeing this pattern in all the controller methods. Same variables are declared in the beginning and the code is placed inside the try catch block which is also same across all the methods. I was wondering if there's a way to push the common code across methods inside a BaseController. So that I don't have to declare the common variables inside each method and the try catch block functionality is also delegated to someplace else.
At first, I created a BaseController class, annotated it with #Controller annotation and extended my controller to be its subclass. Then I moved all the common variables to the BaseController. The problem is, once I modify these variables inside the controller's method, they retain their values even in the next request which is problematic.
#RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public ResponseEntity delete(#PathVariable("id") Integer id)
{
HashMap response = new HashMap();
boolean success = false;
List errors = new ArrayList();
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
String message = "";
try
{
purchaseInvoiceService.delete(id);
success = true;
message = "Purchase Invoice Deleted";
httpStatus = HttpStatus.OK;
} catch (Exception e)
{
errors.add(new ErrorMessage("error", e.getMessage()));
e.printStackTrace();
}
response.put("success", success);
response.put("errors", errors);
response.put("message", message);
return new ResponseEntity(response, httpStatus);
}
I want to refactor this patter so that in each method I just have to contain only the call to the service and conditionally setting the success and httpstatus variable (present in BaseController) and then returning the response using response() method present in BaseController which adds the data variable and it's return type is ResponseEntity.
Edit 1:
This endpoint returns a list of all purchase invoices, currently, its just returning the HashMap which gets converted to JSON. The point I'm trying to make is that these response, success, errors, httpStatus variables and the part where all these variables are put in response HashMap() are a part of every method inside each controller, I'd like to refactor these to something similar to ResponseFactory as well. So I'm thinking to pass the List to ResponseFactory as well which will then structure all the response and return in the form of ResponseEntity. Just want to know if I'm doing it correctly.
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity getAll() {
HashMap response = new HashMap();
boolean success = false;
List errors = new ArrayList();
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
String message = "";
Map data = new HashMap();
try {
List<PurchaseInvoice> purchaseInvoices = purchaseInvoiceService.getAll();
data.put("purchaseInvoices", purchaseInvoices);
success = true;
message = "Purchase Invoice List";
httpStatus = httpStatus.OK;
} catch (Exception e) {
errors.add(new ErrorMessage("error", e.getMessage()));
e.printStackTrace();
}
response.put("success", success);
response.put("errors", errors);
response.put("message", message);
response.put("data", data);
return new ResponseEntity(response, httpStatus);
}
Your phrase: "Then I moved all the common variables to the BaseController" sounds confusing.
A controller in spring is just a Singleton with an additional "ability" provided by spring: its something that is exposed as a web endpoint (less relevant for your specific question).
Being Singleton means that there is one instance in the ApplicationContext.
So if the variables were moved like this:
class BaseController {
protected Field1 field1;
protected Field2 field2;
....
}
Then there is certainly a problem, you've actually introduced a state to the controller, and this state is shared among all the requests.
Long story short, don't create stateful controllers
Having said that the idea of refactoring is good. Just the way to refactor probably is wrong:
Instead, I suggest to consider the following refactoring:
Create class responseFactory with some static methods:
class ResponseFactory {
public static ResponseEntity success(String message) {
here prepare the map with boolean success and everything
}
}
Now the controller becomes:
class Controller {
public ResponseEntity delete(#PathVariable("id") Integer id) {
purchaseInvoiceService.delete(id); // I'll talk about exceptions later
return ResponseEntityFactory.success("Purchase Invoice Deleted");
}
}
Now as for exceptions - this is somewhat confusing - the code basically says that the response would be successful despite the errors.
So if you have to leave it like this, the ResponseEntityFactory will have to get the List of errors as well or something, but in general, Spring has a pretty powerful exception handling mechanism to map the exceptions thrown on the backend (service, DAO, whatever) to the meaningful non-200 response.
I have a method annotated with #PatchMapping.
#PatchMapping(path= "/api/transaction/{transaction-id}/return")
public ResponseEntity<Transaction> returnBookTransaction(#PathVariable(name="transaction-id") Long transactionId){
Transaction transaction = transactionRepository.findById(transactionId).get();
transaction.setDateOfReturn(LocalDateTime.now());
return ResponseEntity.ok().body(transaction);
}
I need to test this method. In test method, I need to use TestRestController.patchForObject();
#Test
public void testReturnBookTransaction() throws Exception {
ResponseEntity<Transaction> response = testRestTemplate.patchForObject("/api/transaction/{transaction-id}/return",
, Transaction.class, 1);
Assert.assertEquals(200, response.getStatusCode().value());
}
The code above shows compiler error in template.patchForObject(), as it needs 4 parameters. I don't know, what to pass in 4th parameter.
I just have to pass 1 for {transaction-id}, URL String and return type class, which I have already passed. But this method requires one more parameter as Object.
I don't know, what to pass in Object.
From Spring's documentation:
public T patchForObject(String url,
Object request,
Class responseType,
Map uriVariables)
throws RestClientException
The second argument is for request object. Since you don't have any request-related info in your controller method, you can set it to null.
Try the following:
Transaction response = testRestTemplate.patchForObject("/api/transaction/{transaction-id}/return", null
, Transaction.class, 1);
UPDATE:
If you want to have access to the response entity, try the following:
ResponseEntity<Transaction> response = testRestTemplate.exchange("/api/transaction/{transaction-id}/return", HttpMethod.PATCH, null,Transaction.class, 1);
I have this Jersey2-based application, with a custom ContainerRequestFilter.
When the filter(ContainerRequestContext) method is called I want to do a check and, if needed, I want to be able to stop the request before entering the main logic of the application.
At the moment I'm using the ContainerRequestContext#abortWith method to block the call and return an "error" response to the client.
My application returns JSONP to the client, and if I block with abortWith the response is always a JSON.
Looking at the jersey sources I found
org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor that is responsible of the JSONP serialization.
In the abortWith flow I see it fails to find the JSONP annotation, but I don't know where it search for it.
My method has it, in fact in the "normal" scenario (without the abortWith) I see correctly the JSONP format.
I found the solution.
The ContainerRequestFilter#filter method was something like
public void filter(final ContainerRequestContext crc) throws IOException {
if (/* logic */) {
CustomObject ret = new CustomObject();
ret.error = "error message";
crc.abortWith(Response.ok(ret)).build());
}
}
JsonWithPaddingInterceptor expected a response with a JSONP annotation so I retrieve them from the ResourceInfo#resourceMethod, with something like
public void filter(final ContainerRequestContext crc) throws IOException {
if (/* logic */) {
Annotation[] as = this.resourceInfo.getResourceMethod().getAnnotations();
CustomObject ret = new CustomObject();
ret.error = "error message";
crc.abortWith(Response.ok().entity(ret, as).build());
}
}
this way the annotation is correctly found
I just noticed a weird problem as I've been testing my application. I was accidentally POSTing to a method that accepts HTTP GET (It was a typo - I'm a little tired), but the weird thing is that Spring was executing a GET action anyway - it wasn't throwing an error.
Here is the mapping for my GET action that I was POSTing to instead:
#RequestMapping(value = "/partialMapping/{partialMappingId}/edit", method = RequestMethod.GET)
public ModelAndView edit(#PathVariable long partialMappingId) {
return new ModelAndView(view("edit"), "partialMapping",
partialMappingService.findPartialMapping(partialMappingId));
}
What I would have expected was for Spring to say, "There is no action called /partialMapping/{partialMappingId}/edit for HTTP POST".
Instead... if you use the HandlerAdapter and pass it "POST" and "/partialMapping/1/edit", it runs my index action instead ("/partialMapping"). It doesn't throw an error. Why?
Is this a bug in spring, or is this desired behaviour? It's not a big deal when it comes to production code, but it surely makes debugging problems harder.
Here is the code I am using to execute a controller action in my tests:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) {
try {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
I found this code per another answer to a question on this site.
I believe your test code mimics the dispatch step that tries to find a matching Controller method signature after the URL and HTTP method have resolved. In other words, you are not testing your controller at the right level if you want to test the HTTP message bindings. For that kind of testing you would probably want to deploy to a server (perhaps embedded Jetty inside your test) and use RestTemplate to call it. That's what I do anyway.
If you annotate with Spring MVC annotations as below
#RequestMapping(method = RequestMethod.GET it should work.