Mocking a void method in the Spring Framework (Mockito) - java

I'm writing integration tests for a spring web app and I have reached a step where I need to mock service methods calls which have a void return type. I've done some research on some ways to do this but none seem to be the correct way.
What I want to do is:
When the save() method is called on recipeService, it should save the recipe
Below I'll provide the code and also the two main ways I've tried already. If anyone can help that would be great!
The method that needs mocking
#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";
}
The service that needs calling (save method)
#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);
}
}
Attempt 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"));
}
Attempt 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());
}
Complete test code so far
#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;
}
}

try Mockito.doNothing(): it basically tells Mockito to do nothing when a method in a mock object is called:
Mockito.doNothing().when(recipeService).save(any(Recipe.class), any(MultipartFile.class));

If you are mocking the save method I would use one of the ‘do...‘
Docs
Of course this suggests your method has a side effect somewhere.
If you want to ensure a method is called the you can use ‘verify‘ as mentioned in other answers.
Generally speaking mocking allows you to replace some collaboration/functionality with a version which is under the tests control where as verifying allows for checking something occurred (or didn’t)

If you have some logic that you want to unit test and this logic invokes methods of other component that you want to mock and some of those methods return void - the typical way of testing your logic is to verify that your logic actually invoked the void methods of mocked object. You can achieve this by using Mockito::verify :
Mockito.verify(recipeService, Mockito.times(1)).save(any(Recipe.class), any(MultipartFile.class));
This way you test that logic of persistRecipe() method actually invoked the desired method on your mock object.

Related

How to write JUnit test case for void method

I am trying to write Junit test cases for a void method. This method is used for updating values in Database. I have tried certain test cases and its returning a success. But when I check the coverage its showing zero. Can anyone tell me the proper way to write test cases for void methods.
this is my service class :
public class CustomerServiceImpl implements CustomerService {
#Autowired
ERepository eRepository;
#Autowired
ActivityUtil activityUtil;
#Override
public void updateCustomer(RequestDTO requestDTO)
throws CustomAException {
if (Objects.nonNull(requestDTO.getAdmissionId())) {
Optional<Admission> optionalAdmission = eRepository.findById(
requestDTO.getAdmissionId());
if (optionalAdmission .isPresent()) {
EAdmission eAdmission = optionalAdmission.get();
updateCustomer(requestDTO, eAdmission);
} else {
throw new CustomAException ("Admission details not found");
}
}
else {
throw new CustomAException ("Admission id not found");
}
}
private void updateCustomer(RequestDTO requestDTO,
EAdmission eAdmission)
throws CustomAException {
logger.info("updating customer info");
try {
if (ObjectUtils.isNotEmpty(eAdmission.getCustomer())) {
eAdmission.getCustomer().setCustomerEmailAddress(
requestDTO.getEmail());
eAdmission.getCustomer().setCorporateTelephoneNumber(
requestDTO.getCustomerPhone());
eAdmission.getCustomer().setZipCode(requestDTO.getZipCode());
eAdmission.getCustomer().setCustomerAddress1(requestDTO.getAddress1());
evfAdmissionRepository.save(evfAdmission);
activityUtil.createActivityLog(eAdmission, Constants.ENTRY_COMPLETED);
} else {
throw new CustomAException ("Customer details not found ");
}
} catch (Exception exception) {
logger.error(Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
throw new CustomAException (Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
}
I am trying to write test cases for updateCustomer but my test class has zero coverage even though its a success.
test class :
#SpringBootTest
public class CustomerServiceImplTest {
#InjectMocks
CustomerServiceImpl CustomerServiceImpl;
#Mock
ERepository eRepository ;
#Mock
ActivityUtil activityUtil;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCustomerException() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
when(evfAdmissionRepository.findById(any())).thenThrow(ConstraintViolationException.class);
Mockito.doThrow(CustomAException .class).when(CustomerServiceImplTest).updateCustomer(setRequestDTO());
}
#Test
public void updateCustomerSuccess() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
CustomerServiceImplTest .updateCustomer(setRequestDTO());
//doNothing().when(evfCustomerServiceImpl).updateEVFCustomerOnSubscribe(any());
verify(CustomerServiceImplTest ).updateCustomerOn(setRequestDTO());
}
private RequestDTO setRequestDTO() {
RequestDTO eRequestDTO = new RequestDTO ();
eRequestDTO .setEmail("test");
// rest of code for setting value
return eRequestDTO ;
}
ArgumentCaptor can be used to capture and Assert Arguments in your method. you can read about ArgumentCaptor here

Mocking a void method in the same class that is under test with mockito and junit5?

i want to mock a void method in the same class that is under test with mockito.
i can do that for not void method with annotating test class with #Spy and then use below code to return data i want.
willAnswer(arg -> arg.getArgument(0))
.given(customerService)
.saveCustomer(any(Customer.class));
but how can do that for void method.
(i already try willDoNothing().given() function)
this is my real method:
public void deleteCustomersByTenantId(TenantId tenantId) throws ThingsboardException {
log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
customersByTenantRemover.removeEntities(tenantId, tenantId);
}
public void deleteCustomer(TenantId tenantId, CustomerId customerId) throws ThingsboardException {
log.trace("Executing deleteCustomer [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
Customer customer = findCustomerById(tenantId, customerId);
if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer.");
}
entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
deleteEntityRelations(tenantId, customerId);
entityGroupDao.deleteEntityGroupsByTenantIdAndCustomerId(customer.getTenantId(), customerId);
customerDao.removeById(tenantId, customerId.getId());
}
private final PaginatedRemover<TenantId, Customer> customersByTenantRemover =
new PaginatedRemover<>() {
#Override
protected PageData<Customer> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
return customerDao.findCustomersByTenantId(id.getId(), pageLink);
}
#Override
protected void removeEntity(TenantId tenantId, Customer entity) throws ThingsboardException {
deleteCustomer(tenantId, new CustomerId(entity.getUuidId()));
}
};
and this is the test:
void deleteCustomersByTenantId() throws ThingsboardException {
//given
Customer customer = new Customer();
customer.setId(CUSTOMER_ID);
customer.setTenantId(TENANT_ID);
ArgumentCaptor<CustomerId> customerIdArgumentCaptor = ArgumentCaptor.forClass(CustomerId.class);
var pageData = new PageData<>(Collections.singletonList(customer), 1 ,1 , false);
given(customerDao.findCustomersByTenantId(any(UUID.class), any(PageLink.class)))
.willReturn(pageData);
doNothing()
.when(customerService)
.deleteCustomer(any(), any());
//when
customerService.deleteCustomersByTenantId(TENANT_ID);
//then
then(customerDao)
.should(times(1))
.findCustomersByTenantId(any(UUID.class), any(PageLink.class));
then(customerService)
.should(times(1))
.deleteCustomer(any(TenantId.class), customerIdArgumentCaptor.capture());
assertEquals(CUSTOMER_ID, customerIdArgumentCaptor.getValue());
}
this is the Mocks:
//other mocks...
#Mock
private RelationService relationService;
#Mock
private CustomerValidator customerValidator;
#InjectMocks
#Spy
private CustomerServiceImpl customerService;
every dependency is mock with #Mock.
Do nothing works for me. Just pay attention to the calling order
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
doNothing().when(yourMock).yourMethod(yourArgs);
This thread treats a similar problem, using a spy to mock specific methods with static factories should work. I've seen people doing to reverse operation (Use a Mock and then call the real method in some specific cases), but never tried it personally..

How to verify if method was called from other by mockito

I am trying to mock the behavior of a method that is called inside another so that it simulates the return of an object and another time it raises an exception but not if exactly if it is possible and if it is how it would be possible.
#Service
#Transactional
public class CategoryService {
#Autowired
private CategoryRepository repository;
public Category findById(Integer id) {
Optional<Category> obj = repository.findById(id);
return obj.orElseThrow(() -> new ObjectNotFoundException(id.toString()));
}
public Category update(Category category){
// Throws ObjectNotFoundException if not found before update
this.findById(category.getId());
return repository.save(category);
}
}
#RunWith(MockitoJUnitRunner.class)
public class CategoryServiceUnitTest {
#Mock
private CategoryService service;
#Test()
public void Should_UpdateCategory_When_FindCategory() {
Category cat = new Category(1, "Test");
//Is it possible?
when(service.findById(Mockito.anyInt())).thenReturn(cat);
Category category = service.update(cat);
assertThat(category.getName()).isEqualTo(cat.getName());
verify(service, times(1)).update(cat);
}
#Test(expected = ObjectNotFoundException.class)
public void Should_ThrowsObjectNotFoundException_When_NotFoudCategoryById() {
Category cat = new Category(1, "Test");
//Is it possible?
when(service.findById(Mockito.anyInt())).thenThrow(ObjectNotFoundException.class);
service.update(cat);
}
}
As pointed out in the comments, what you want to do is mock CategoryRepository in your test.
#RunWith(MockitoJUnitRunner.class)
public class CategoryServiceTest {
private CategoryService service;
#Mock
private CategoryRepository repository;
#Before
public void setup() {
service = spy(new CategoryService(repository));
}
#Test
public void Should_UpdateCategory_When_FindCategory() throws ObjectNotFoundException {
Category cat = new Category(1, "Test");
when(repository.findById(Mockito.anyLong())).thenReturn(Optional.of(cat));
//return the category object that is used to call the repository.save(...) method
when(repository.save(Mockito.any(Category.class)))
.thenAnswer((Answer<Category>) invocation -> {
Object[] args = invocation.getArguments();
return (Category) args[0];
}
);
//depending on your requirements the above might be overkill, just replace that logic with this
//when(repository.save(Mockito.any(Category.class))).thenReturn(cat);
Category category = service.update(cat);
assertThat(category).isNotNull();
assertThat(category.getName()).isEqualTo(cat.getName());
verify(service).update(cat);
}
#Test(expected = ObjectNotFoundException.class)
public void Should_ThrowsObjectNotFoundException_When_NotFoudCategoryById() throws ObjectNotFoundException {
Category cat = new Category(1, "Test");
when(service.findById(Mockito.anyLong())).thenThrow(ObjectNotFoundException.class);
service.update(cat);
}
}
You'll also need to handle the checked exception ObjectNotFoundException. I just added the exception to the method signature, you might want to handle it differently in a production setting
#Service
#Transactional
public class CategoryService {
private final CategoryRepository repository;
#Autowired
public CategoryService(CategoryRepository repository) {
this.repository = repository;
}
public Category findById(Long id) throws ObjectNotFoundException {
Optional<Category> obj = repository.findById(id);
return obj.orElseThrow(() -> new ObjectNotFoundException(id.toString()));
}
public Category update(Category category) throws ObjectNotFoundException {
// Throws ObjectNotFoundException if not found before update
this.findById(category.getId());
return repository.save(category);
}
}

Unit test case for a multipart file upload post method in spring controller

pls find below my controller class and service class
#RequestMapping(value = "/offers/{jobTitle}/applications", method = RequestMethod.POST, consumes = {
"multipart/form-data" })
public ResponseEntity<Object> uploadMultipartFile(#RequestPart("file") MultipartFile file,
#PathVariable String jobTitle, #RequestParam("applicationStatus") String applicationStatus,
#RequestParam("name") String name, #RequestParam("emailId") String emailId) throws IOException {
Application app = applicationService.createApplicationMultipartFile(file, jobTitle, applicationStatus, name,
emailId);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}")
.buildAndExpand(app.getOffer().getJobTitle()).toUri();
return ResponseEntity.created(location).body(app);
}
And service class is
#Override
public Application createApplicationMultipartFile(MultipartFile file, String jobTitle, String applicationStatus,
String name, String emailId) throws IOException {
if (!offerRepository.existsById(jobTitle)) {
throw new ResourceNotFoundException("JobTitle " + jobTitle + " not found !!");
}
List<String> emailIds = new ArrayList<>();
List<Application> appliedApplications = applicationRepository.findByOfferJobTitle(jobTitle);
for (Application app : appliedApplications) {
emailIds.add(app.getEmailId());
}
if (emailIds.contains(emailId)) {
throw new ExistingResourceException("User " + emailId + " has already applied for the given Post !!");
}
Offer offer = offerRepository.findById(jobTitle).get();
Application application = new Application();
application.setApplicationStatus(ApplicationStatus.valueOf(applicationStatus));
application.setResume(file.getBytes());
application.setName(name);
application.setEmailId(emailId);
application.setOffer(offer);
return applicationRepository.save(application);
}
i want to write unit test case for controller. i am using testNg and mockito for this.
below is my understanding
public class ApplicationControllerTest {
private MockMvc mvc;
private JacksonTester<Application> jsonApplication;
#Mock
ApplicationService appService;
#InjectMocks
ApplicationController appController;
private Offer offer;
private Application app1;
List<Application> appList1;
#BeforeMethod
public void setup() {
offer = new Offer("LSE", new Date(),1);
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(appController)
.build();
JacksonTester.initFields(this, new ObjectMapper());
}
#Test
public void canCreateANewApplicationMultiPart() throws Exception {
Mockito.when(appService.createApplicationMultipartFile(Mockito.any(MultipartFile.class), Mockito.eq("LSE"), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class))).thenReturn(app1);
MockHttpServletResponse response = mvc.perform(post("/offers/LSE/applications").contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.content(jsonApplication.write(new Application("john","john123","res".getBytes(),offer,ApplicationStatus.APPLIED)).getJson())).andReturn().getResponse();
assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());
assertThat(response.getContentAsString()).isEqualTo(new ObjectMapper().writeValueAsString(app1));
}
i guess my controller is expecting inputs in #requestParam thats why i m getting error. if possible give the testcases for this controller method

How to write mockito junit for the method below:

Help
#Override
public String postRequestinTransactionService(String data) {
RequestTransaction request = new RequestTransaction(data.getClass().getName(), data);
HttpEntity<RequestTransaction> entity = new HttpEntity<RequestTransaction>(request);
ResponseEntity<String> response = restTemplate.exchange(this.urlTransactions, HttpMethod.POST, entity,
String.class);
return response.getBody();
}
Here is barebone test class for you. You can write test case and if you have specific problem then ask question.
#RunWith(MockitoJUnitRunner.class)
public class YourClassNameTest{
#InjectMocks
YourClassUnderTest myClass;
private String data;
#Before
public void setUp() throws Exception {
//prepare you data here
// any other mock action you can set here
}
#Test
public void testPostRequestinTransactionService() throws Exception {
//Write you test here
String result=myClass.postRequestinTransactionService(data);
assertThat("result should be blablabla", result, is("blablabla");
}

Categories

Resources