[英]Mocking a void method in the Spring Framework (Mockito)
我正在为Spring Web应用程序编写集成测试,并且已经达到了需要模拟具有无效返回类型的服务方法调用的步骤。 我已经对某些方法进行了研究,但似乎没有一个是正确的方法。
我想做的是:
下面,我将提供代码以及已经尝试过的两种主要方法。 如果有人可以帮助,那就太好了!
需要模拟的方法
@RequestMapping(path = "/recipes/add", method = RequestMethod.POST)
public String persistRecipe(@Valid Recipe recipe, BindingResult result, @RequestParam("image") MultipartFile photo, RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
redirectAttributes.addFlashAttribute("recipe", recipe);
redirectAttributes.addFlashAttribute("flash",
new FlashMessage("I think you missed something. Try again!", FlashMessage.Status.FAILURE));
return "redirect:/recipes/add";
}
User user = getUser();
recipe.setOwner(user);
user.addFavorite(recipe);
recipeService.save(recipe, photo);
userService.save(user);
redirectAttributes.addFlashAttribute("flash", new FlashMessage("The recipe has successfully been created", FlashMessage.Status.SUCCESS));
return "redirect:/recipes";
}
需要调用的服务(保存方法)
@Service
public class RecipeServiceImpl implements RecipeService {
private final RecipeRepository recipes;
@Autowired
public RecipeServiceImpl(RecipeRepository recipes) {
this.recipes = recipes;
}
@Override
public void save(Recipe recipe, byte[] photo) {
recipe.setPhoto(photo);
recipes.save(recipe);
}
@Override
public void save(Recipe recipe, MultipartFile photo) {
try {
recipe.setPhoto(photo.getBytes());
recipes.save(recipe);
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public Recipe findById(Long id) {
Optional<Recipe> recipe = recipes.findById(id);
if (recipe.isPresent()) {
return recipe.get();
}
// TODO:drt - Create new exception to handle this
throw new RuntimeException();
}
@Override
public Recipe findByName(String name) {
return null;
}
@Override
public List<Recipe> findAll() {
return (List<Recipe>) recipes.findAll();
}
@Override
public void deleteById(Long id) {
recipes.deleteById(id);
}
}
尝试1
@Test
@WithMockUser(value = "daniel")
public void createNewRecipeRedirects() throws Exception {
User user = userBuilder();
Recipe recipe = recipeBuilder(1L);
recipe.setOwner(user);
user.addFavorite(recipe);
MockMultipartFile photo = new MockMultipartFile("image", "food.jpeg",
"image/png", "test image".getBytes());
when(userService.findByUsername("daniel")).thenReturn(user);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {
Recipe recipe1 = (Recipe) arguments[0];
MultipartFile file = (MultipartFile) arguments[1];
recipe1.setPhoto(file.getBytes());
}
return null;
}
}).when(recipeService).save(any(Recipe.class), any(MultipartFile.class));
mockMvc.perform(post("/recipes/add"))
.andExpect(redirectedUrl("/recipes"))
.andExpect(flash().attributeExists("flash"));
}
尝试2
@Test
@WithMockUser(value = "daniel")
public void createNewRecipeRedirects() throws Exception {
List<Recipe> recipes = recipeListBuilder();
List<User> users = new ArrayList<>();
User user = userBuilder();
Recipe recipe = recipeBuilder(1L);
recipe.setOwner(user);
user.addFavorite(recipe);
MockMultipartFile photo = new MockMultipartFile("image", "food.jpeg",
"image/png", "test image".getBytes());
when(userService.findByUsername("daniel")).thenReturn(user);
doAnswer(answer -> {
recipe.setPhoto(photo.getBytes());
recipes.add(recipe);
return true;
}).when(recipeService).save(any(Recipe.class), any(MultipartFile.class));
doAnswer(answer -> {
users.add(user);
return true;
}).when(userService).save(any(User.class));
mockMvc.perform(post("/recipes/add"))
.andExpect(redirectedUrl("/recipes"))
.andExpect(flash().attributeExists("flash"));
assertEquals(3, recipes.size());
assertEquals(1, users.size());
}
到目前为止的完整测试代码
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class RecipeControllerTests {
private MockMvc mockMvc;
@Mock
private RecipeService recipeService;
@Mock
private UserService userService;
@Mock
private IngredientService ingredientService;
@Autowired
WebApplicationContext wac;
@InjectMocks
private RecipeController recipeController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(springSecurity()).build();
}
/**
* Tests for index pages / & /recipes
*/
@Test
@WithUserDetails(value = "daniel")
public void indexPageLoads() throws Exception {
List<Recipe> recipes = recipeListBuilder();
List<Ingredient> ingredients = ingredientsListBuilder();
when(recipeService.findAll()).thenReturn(recipes);
when(ingredientService.findAll()).thenReturn(ingredients);
when(userService.findByUsername("daniel")).thenReturn(userBuilder());
mockMvc.perform(get("/recipes"))
.andExpect(model().attributeExists("recipes", "ingredients", "favs"))
.andExpect(status().isOk());
}
/**
* Tests for page /recipes/add
*/
@Test
@WithMockUser
public void addRecipePageLoads() throws Exception {
mockMvc.perform(get("/recipes/add"))
.andExpect(model().attributeExists("task", "buttonAction", "action", "photo", "recipe"))
.andExpect(status().isOk());
}
@Test
@WithUserDetails("daniel")
public void createNewRecipeRedirects() throws Exception {
User user = userBuilder();
Recipe recipe = recipeBuilder(1L);
recipe.setOwner(user);
user.addFavorite(recipe);
MultipartFile photo = new MockMultipartFile("image", "food.jpeg",
"image/jpeg", "dummy content file".getBytes());
when(userService.findByUsername("daniel")).thenReturn(user);
verify(recipeService, times(1)).save(recipe, photo);
verify(userService, times(1)).save(user);
mockMvc.perform(post("/recipes/add"))
.andExpect(redirectedUrl("/recipes"))
.andExpect(flash().attributeExists("flash"));
}
private User userBuilder() {
User user = new User();
user.setFavorites(recipeListBuilder());
user.setId(1L);
user.setRoles(new String[]{"ROLE_USER", "ROLE_ADMIN"});
user.setUsername("daniel");
user.setPassword("password");
return user;
}
private List<Recipe> recipeListBuilder() {
List<Recipe> recipes = new ArrayList<>();
recipes.add(recipeBuilder(1L));
recipes.add(recipeBuilder(2L));
return recipes;
}
private List<Ingredient> ingredientsListBuilder() {
List<Ingredient> ingredients = new ArrayList<>();
ingredients.add(ingredientBuilder());
return ingredients;
}
private Ingredient ingredientBuilder() {
Ingredient ingredient = new Ingredient();
ingredient.setCondition("good");
ingredient.setName("test ing");
ingredient.setQuantity(1);
ingredient.setId(1L);
return ingredient;
}
private Recipe recipeBuilder(Long id) {
Recipe recipe = new Recipe();
recipe.setName("Test recipe");
recipe.setDescription("Test Description");
recipe.setId(id);
recipe.setCategory(Category.ALL_CATEGORIES);
recipe.setCookTime(10);
recipe.setPrepTime(10);
recipe.addIngredient(ingredientBuilder());
return recipe;
}
}
如果您有一些要进行单元测试的逻辑,并且此逻辑调用您要模拟的其他组件的方法,并且其中一些方法返回void
测试逻辑的典型方法是验证您的逻辑是否实际调用了void
方法被嘲笑的对象。 您可以使用Mockito::verify
实现此Mockito::verify
:
Mockito.verify(recipeService, Mockito.times(1)).save(any(Recipe.class), any(MultipartFile.class));
这样,您可以测试persistRecipe()
方法的逻辑实际上在模拟对象上调用了所需的方法。
如果您要模拟保存方法,则可以使用“ do ...” 文档之一 。当然,这表明您的方法在某些地方有副作用。
如果您想确保方法被调用,可以使用其他答案中提到的“验证”。
一般来说,模拟可让您用测试控制下的版本替换某些协作/功能,而验证可检查是否已发生(或未发生)
试试Mockito.doNothing()
:当调用模拟对象中的方法时,它基本上告诉Mockito不执行任何操作:
Mockito.doNothing().when(recipeService).save(any(Recipe.class), any(MultipartFile.class));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.