Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java

RedirectAttributes and Tests

I need test my controller method. Code of controller:

@RequestMapping(path="/add", method = RequestMethod.POST) public RedirectView addToCart(@ModelAttribute(value="productId") long productId, @ModelAttribute(value="quantity") int quantity, RedirectAttributes redirectAttributes) throws ProductNotFoundException {

RedirectView redirect = new RedirectView("/product/"); redirect.setExposeModelAttributes(false);

try { redirectAttributes.addFlashAttribute("flash", shoppingCartService.addQuantity(sCart, productId, quantity)); } catch (ExceedsProductQuantityException e) { e.printStackTrace(); redirectAttributes.addFlashAttribute("flash", new FlashMessage(e.getMessage(), FlashMessage.Status.FAILURE)); }

return redirect; }

My test code looks like:

@Test(expected = ExceedsProductQuantityException.class) public void addTooManyToCartTest1() throws Exception { Product product = productBuilder(); product.setQuantity(15);

Purchase purchase = purchaseBuilder(product); // First purchase

when(productService.findById(1L)).thenReturn(product);
when(sCart.getPurchase()).thenReturn(purchase);

mockMvc.perform(MockMvcRequestBuilders.post("/cart/add")
    .param("quantity", String.valueOf(product.getQuantity() + 1))
    .param("productId", "1"))
    .andExpect(MockMvcResultMatchers.model().attribute("flash", "rdValue"))
    .andExpect(MockMvcResultMatchers.flash().attribute("flash", FlashMessage.class));

}

But I get NestedServledException error message, I think its because in my controller method I try to work with RedirectedAttributes, but it's null. So, where and how I have to init and set RedirectedAttributes in my test?

4 Answers

I looked at your code. No offence taken. But you put a bit too much on Service Layer... Anyway... Coming back to your error. In your Controller method you use shoppingCartService.addQuantity(...). When you use service in testing you have to @Autowire it. I added

@Mock
private ShoppingCartService shoppingCartService;

And that changed error to normal Assertion error. Try that out. And remember that everything you @Autowire in controller has to be @Mock -ed in Testing.

Yep, you right. Found that error yesterday :). Rewrite @ExceptionHandler right now, what is view range for @ExceptionHandler? I mean do I have to write copy of it for each class/package?

  1. It is a very bad idea to use try..catch block in @RequestMapping Controller method. Use @ExceptionHandler method: Did you watch "Unit Testing in Spring"? There is FavoriteController.notFound example: https://github.com/treehouse-projects/spring-unit-test-weather/blob/master/src/main/java/com/teamtreehouse/web/controller/FavoriteController.java

  2. Use doPrint method, to see what exactly your request looks like, There are a lot of tests like this in our project, it will help you.

  3. About the Error. It is highly likely that there is something other wrong. Redirect Attributes if they are null. You will get Unit Test Assertion Error, not ServletException - which is more severe.

  4. To see what exactly the problem is share link to your GitHub project. Then Anyone can take a look, Otherwise the structure of the project is too complicated to make judgement of Error, just by couple of lines that you've shown.

I'm following 'Spring Unit Testing' Structure of App, considering @ExceptionHandler:

About your question what is view range for @ExceptionHandler? I mean do I have to write copy of it for each class/package?... I don't really understand what you mean. Exception is usually defined in separate package, like com.teamtreehouse.exception. When it is thrown in controller method, @ExceptionHandler method checks all the methods for exceptions specified in @Exception(SomeException.class) annotation argument. Important thing is to add Exception exception to arguments of the @ExceptionHandler method. Take a look at the links above. When you do that you can access Exception thrown in the body of your @ExceptionHandler method. Again take a look how it is done. Also nice theoretical introduction in Spring Docs, if you need:

https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

I've asked about case when same exception (like in my case) thrown in differrent controllers (ProductNotFoundException thrown in ProductController, CartController and even in CheckoutController), so question was have I to rewrite same @ExceptionHandler in differrent controllers (since they are in diff classes/files?) Thanks for your help anyway. And we can speak russian :)

Yes, I think you have to rewrite @ExceptionHandler in each controller, I guess, because controllers are not linked in any ways. I'm sure there probably is the way to generalize @ExceptionHandler for all controllers, but I don't know it. I don't know the rules about language here, so lets leave it english :) Glad that can be of some help.