Spring Controller testing

Viewed 48

I am trying to test a post method called saveProduct in my springboot application. It takes params like product, bindingResult, model, multipartFile, redirectAttributes. The product has an attribute photo.

The issue is I am getting an http status is 400 when it is supposed to be 200. I looked at the several online documentation on how to test post api but I still cant figure it out. This is one of the documentation I was looking at on how to test a post multipart uploading springboot application. https://spring.io/guides/gs/uploading-files/

Controller class (method)

@PostMapping("/showNewProductForm")
    public String addAProduct(@Valid @ModelAttribute("saved") Product product,
                              BindingResult bindingResult,
                              Model model,
                              @RequestParam("image") MultipartFile file,
                              RedirectAttributes redirectAttributes) {
        return productService
                .saveProduct(product, bindingResult, model, file, redirectAttributes);
    }

Service class (method)

public String saveProduct(Product product,
                              BindingResult bindingResult,
                              Model model,
                              MultipartFile file,
                              RedirectAttributes redirectAttributes) {
        Optional<Product> productByName = productRepository.findProductByName(product.getName());

        if (bindingResult.hasErrors()) {
            return "add-new-product"; // For binding error, you always return to the form instead of the get API
        } else if (productByName.isPresent()) {
            redirectAttributes.addFlashAttribute("message",
                    product.getName() + " already exists, please enter a different name");
            return "redirect:/showNewProductForm";
        }
        boolean checkImageUploadStatus = addingImageLogic(product, file);
        if (!checkImageUploadStatus) {
            redirectAttributes.addFlashAttribute("message", "Please enter an image for " + product.getName());
            return "redirect:/showNewProductForm";
        }
        productRepository.save(product);
        model.addAttribute("saved", product);
        redirectAttributes.addFlashAttribute("message",
                "You successfully uploaded " + product.getName() + "!");
        return "redirect:/showNewProductForm";
    }

Test class

@WebMvcTest(ProductController.class)
class ProductControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ProductService productService;


    @Test
    void addAProduct() throws Exception {
        /*
        *  Product parameters are
        *  product (name, category, description, photo)
        */
        Product product = new Product();
        product.setName("Test Run");
        product.setCategory("Dairy");
        product.setDescription("Lorem 24");
        Model model = mock(Model.class);
        BindingResult bindingResult = mock(BindingResult.class);
        RedirectAttributes redirectAttributes = mock(RedirectAttributes.class);

        // When
        MockMultipartFile mockMultipartFile = new MockMultipartFile(
                "image",
                "green-bean.jpg",
                "image/png, image/jpg, image/jpeg",
                "random img".getBytes()
        );
        when(productService
                .saveProduct(product, bindingResult, model, mockMultipartFile, redirectAttributes))
                .thenReturn(String.valueOf(product));

        // then

        this.mockMvc
                .perform(multipart("/showNewProductForm")
                        .file(mockMultipartFile))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is("Test Run")));

        then(this.productService)
                .should()
                .saveProduct(product, bindingResult, model, mockMultipartFile, redirectAttributes);
    }

}

The error message

org.thymeleaf.exceptions.TemplateInputException: Error resolving template [showNewProductForm], template might not exist or might not be accessible by any of the configured Template Resolvers

Structure enter image description here

1 Answers

I figured the issue. The error like it says above is my template name is different. It is best practice to name your api the same as your html page. To solve this error, I had to change the name of my html page to showNewProductForm and also the return string to return for binding error in service class.

if (bindingResult.hasErrors()) {
            return "add-new-product"; // For binding error, you always return to the form instead of the get API
        }

Also there is no need for this (below) in the test method. View the online documentation sample for why it is not needed

when(productService
                .saveProduct(product, bindingResult, model, mockMultipartFile, redirectAttributes))
                .thenReturn(String.valueOf(product));
Related