简体   繁体   中英

Exception is not thrown when using Mockito's doThrow method

I am working with a mocked object like the following:

@Mock
private RecipeService recipeService

I also have the following method inside the test class:

    @Test
    public void testAddRecipeWithNonUniqueName() throws Exception {
        Recipe recipe = new Recipe();

        doThrow(Exception.class)
                .when(recipeService)
                .save(recipe);

        mockMvc.perform(post("/recipes/add-recipe")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .param("id", "1")
                .param("name", "recipe1"))
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name("redirect:/recipes/add"));
    }

As you can see, I am using mockito's doThrow method to decide what exception is going to be thrown when the void method named save is called.

I want to make a POST request using a MockMvc object. So the method marked with the /recipes/add-recipe endpoint will be called inside one of my controller classes. The following snippet of code shows that method in detail:

    @RequestMapping(value = "/recipes/add-recipe", method = RequestMethod.POST)
    public String addRecipe(@Valid Recipe recipe, BindingResult result, RedirectAttributes redirectAttributes,
                            @AuthenticationPrincipal User user){

       String response = validateFormValues(recipe, redirectAttributes, result,
               "redirect:/recipes/add");
       if(!response.isEmpty())return response;

        recipe.setUser(user);

        try {
            recipeService.save(recipe);
        }catch(Exception e){
            redirectAttributes.addFlashAttribute("uniqueConstraintError",
                    String.format("The name \"%s\" is already taken by another recipe. " +
                                    "Please try again!",
                            recipe.getName()));
            return "redirect:/recipes/add";
        }

        setUserForIngredientsAndSteps(recipe);

        redirectAttributes.addFlashAttribute("flash",
                new FlashMessage("The recipe has been added successfully!", FlashMessage.Status.SUCCESS));
        return String.format("redirect:/recipes/%s/detail", recipe.getId());
    }

The above method contains a try-catch block. When the recipeService.save() is called, I am expecting that an exception will be thrown, and handled by the catch block. But that doesn't happen. Instead, the other lines are executed.

What am I missing ?

the doTrhow() method you are calling is triggered only when you save on the specific recipe you created before.

You need to tell Mockito to throw on any recipe

Mockito.doThrow(Exception.class)
            .when(recipeService)
            .save(Mockito.any(Recipe.class));
Recipe recipe = new Recipe();

doThrow(Exception.class)
        .when(recipeService)
        .save(recipe);

This code will only work if the exact same instance of Recipe is passed to the save method. If you have implemented an equals and/or hashCode method passing in a Recipe instance if the expected values 1 and name might make it work.

Recipe recipe = new Recipe();
recipe.setId(1);
recipe.setName("name");

doThrow(Exception.class)
        .when(recipeService)
        .save(recipe);

However as you probably want to test the error situation it is probably easier to always throw an exception. For this use the any() matcher.

doThrow(Exception.class)
        .when(recipeService)
        .save(any(Recipe.class);

Now upon calling save, regardless of the passed in Recipe the exception will be thrown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM