Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

Como realizar o teste do método salvarProduto ?

Com o curso Spring MVC II finalizado, creio que a versão final do método de salvar um produto termine assim:

@RequestMapping(method=RequestMethod.POST)
    @CacheEvict(value="productsHome", allEntries=true)
    public ModelAndView saveProduct(MultipartFile file, @Valid Product product, BindingResult result, RedirectAttributes redirectAttributes) {
        System.out.println(file.getOriginalFilename());

        if (result.hasErrors()) {
            System.out.println(result.getFieldError());
            return form(product);
        }
        String path = fileSaver.write("fileLocation", file);
        product.setFileLocation(path);

        productDAO.save(product);
        redirectAttributes.addFlashAttribute("bookAddedMessage", "Product " + product.getTitle() + " added with success.");
        return new ModelAndView("redirect:/products");
    }

Como seria o teste escrito para este método? Os dois testes explorados no curso foram:

@Test
    public void mustReturnToHomeWithBooks() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/"))
            .andExpect(MockMvcResultMatchers.model().attributeExists("products"))
            .andExpect(MockMvcResultMatchers.forwardedUrl("/WEB-INF/views/products/home.jsp"));
    }

    @Test
    public void onlyAdminUsersShouldAccessForm() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/products/form")
                .with(SecurityMockMvcRequestPostProcessors
                        .user("user@bookstore.com.br").password("123456")
                        .roles("USER")))        
                    .andExpect(MockMvcResultMatchers.status().is(403));
    }

Gostaria muito de receber uma ajuda aqui para escrever um teste para este método que salva um produto.

A dificuldade em criar um produto e como inserir o parâmetro que representa esse produto na requisição mockada que está sendo feita, e ainda testando o método de "ponta-a-ponta", pois o mesmo recebe um produto, salva no banco e redireciona para uma nova view.

Ou seja, bem diferente do que apenas testar um metódo que recebe uma requisição e faz um forward...

Pela dificuldade que tive inicialmente, estou achando que ficará muito mais fácil para eu fazer um teste de aceitação para esse formulário tão importante na aplicação, pois assim esse teste já validará tudo, desde o binding result até o redirecionamento.

A saída é essa mesmo ou existe como testar esse método sem ser via teste de aceitação?

2 respostas
solução!

Fala Davi, tudo bem ?

Bom, testar o controller tem muitp essa cara mesmo, visto que em geral ele não contém em si muita lógica, sendo mais o fluxo de chamadas para outros objetos mesmo.

Pensando na action de salvar produto, imagino que seja justo cobrir as duas possibilidades deste método, que são: a) verificar a recusa de um produto em estado inválido; e b) verificar que tudo correu bem e o produto foi adicionado.

Poderia ficar algo como:

@Test
public void shouldRefuseTheInsertOfABook() throws Exception {
    MockHttpServletRequestBuilder request = MockMvcRequestBuilders
        .post("/products")
        .contentType(MediaType.APPLICATION_FORM_URLENCODED);

    this.mockMvc.perform(request)
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.forwardedUrl("/WEB-INF/views/products/form.jsp"))
            .andExpect(MockMvcResultMatchers.model().attributeHasFieldErrors("product", "title"));
}

@Test
@Transactional
public void shouldVerifyTheInsertionOfABook() throws Exception {

    MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                .post("/products")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .param("title", "Titulo do Livro")
                .param("description", "Descrição do Livro")
                .param("numberOfPages", "50");

    this.mockMvc.perform(request)
            .andExpect(MockMvcResultMatchers.status().is(302))
            .andExpect(MockMvcResultMatchers.redirectedUrl("/products"))
            .andExpect(MockMvcResultMatchers.flash().attribute("successMessage", "Produto "
                + "Titulo do Livro" + " adicionado com sucesso"));
}

Como pode ver, continua com a mesma cara o teste. Verificando justamente as saídas possíveis do modulo controlador, que nos possibilita, mesmo que indiretamente, concluir se os demais módulos estão executando corretamente. Para cobrir os demais recursos, poderiam ser feitos testes dos validators aplicados, ou até mesmo do dao para verificar se a inserção correu bem.

Sua ideia do teste de aceitação também é bem justa, também ajuda a cobrir a funcionalidade. Na maioria dos casos inclusive, o pessoal opta por testar a funcionalidade como um todo do que o controller propriamente dito. Mas teste nunca é demais =) (há controvérsias! rs)

Espero ter ajudado. Abraço!

Rafael Rollo meu amigo, com certeza ajudou! Muito obrigado pela sua atenção mais uma vez.