How to test findById method? - spring

First - I've checked all previous topics around this question and none of them helped.
Having the following code:
#DisplayName("GET RecipeUltraLight by id is successful")
#Test
public void givenRecipeId_whenGetRecipeDetailsById_thenReturnRecipeObject(){
// given
given(this.recipeRepository.findById(recipe.getId())).willReturn(Optional.of(recipe));
given(this.recipeService.getRecipeById(recipe.getId())).willReturn(recipe);
given(this.recipeConverter.toUltraLight(recipe)).willReturn(recipeUltraLightDto);
// when
RecipeUltraLightDto retrievedRecipe = recipeService.getRecipeUltraLightById(recipe.getId());
// then
verify(recipeRepository, times(1)).findById(recipe.getId());
verify(recipeService, times(1)).getRecipeById(recipe.getId());
verify(recipeConverter, times(1)).toUltraLight(recipe);
assertThat(retrievedRecipe).isNotNull();
}
gives me this error:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Recipe cannot be returned by findById()
findById() should return Optional
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
Service method:
#Transactional(readOnly = true)
public RecipeUltraLightDto getRecipeUltraLightById(Long id) {
Recipe recipe = getRecipeById(id);
RecipeUltraLightDto dto = new RecipeUltraLightDto();
dto = recipeConverter.toUltraLight(recipe);
return dto;
}
// internal use only
#Transactional(readOnly = true)
public Recipe getRecipeById(Long id) {
if (id == null || id < 1) {
return null;
}
return recipeRepository.findById(id)
.orElseThrow(() -> new RecipeNotFoundException(
String.format("Recipe with id %d not found.", id)
));
}
Setup:
#ContextConfiguration(classes = {RecipeService.class})
#ExtendWith({SpringExtension.class, MockitoExtension.class})
class RecipeServiceTest {
#MockBean
private RecipeConverter recipeConverter;
#MockBean
private RecipeRepository recipeRepository;
#Autowired
private RecipeService recipeService;
private Recipe recipe;
private RecipeUltraLightDto recipeUltraLightDto;
#BeforeEach
public void setup(){
recipe = Recipe.builder()
.id(1L)
.name("Recipe")
.description("Description")
.createdAt(LocalDateTime.now())
.difficulty(RecipeDifficulty.EASY)
.minutesRequired(60)
.portions(4)
.authorId(1L)
.views(0)
.isVerified(false)
.build();
recipeUltraLightDto = RecipeUltraLightDto.builder()
.id(1L)
.name("Recipe")
.build();
}
I've tried:
Optinal.ofNullable()
Adding .isPresent()
Getting rid of .orElseThrow and going through if statements and using .get()
Kotlin
Will be glad if someone can help.

You are creating a mock of the object you are testing and with that basically also render the mocking of the repository useless.
You should remove the line given(this.recipeService.getRecipeById(recipe.getId())).willReturn(recipe); that way it will just call the method and call the repository. Which now will return the mocked result. As that is the behavior that will now kick in.

It is clearly mentioned that the method findById() returning Optional, you need to get Recipe by invoking Optional.get().

Related

Mockito how to mock Optional.map().orElseThrow()

today I come across below problem-
I'm doing an Udemy course and I try to test below method:
public GroupReadModel createGroup(LocalDateTime deadline, Integer projectId) {
if (!configurationProperties.getTemplate().isAllowMultipleTasks() && taskGroupRepository.existsByDoneIsFalseAndProject_Id(projectId)) {
throw new IllegalStateException("Only one undone group form project is allowed");
}
TaskGroup result = projectRepository.findById(projectId)
.map(project -> {
TaskGroup taskGroup = new TaskGroup();
taskGroup.setDescription(project.getDescription());
taskGroup.setTasks(project.getProjectSteps().stream()
.map(step -> Task.createNewTask(step.getDescription(), deadline.plusDays(step.getDaysToDeadline())))
.collect(Collectors.toSet()));
taskGroup.setProject(project);
return taskGroupRepository.save(taskGroup);
}).orElseThrow(() -> new NoSuchElementException(String.format("No project with ID %d found", projectId)));
return new GroupReadModel(result);
}
Here is test method:
#ExtendWith(SpringExtension.class)
class ProjectServiceTest {
#Autowired
private ProjectService projectService;
#MockBean
private ProjectRepository projectRepository;
#MockBean
private TaskGroupRepository taskGroupRepository;
#MockBean
private TaskConfigurationProperties configurationProperties;
#Mock
private TaskConfigurationProperties.Template template;
#TestConfiguration
static class ProjectServiceTestConfig {
#Bean
ProjectService projectService(ProjectRepository projectRepository, TaskGroupRepository taskGroupRepository, TaskConfigurationProperties configurationProperties ){
return new ProjectService(projectRepository, taskGroupRepository, configurationProperties);
}
}
#Test
void should_return_new_group_read_model() {
//given
LocalDateTime deadline = LocalDateTime.now();
Integer projectId = 99;
Project projectById = new Project(projectId, "test project");
projectById.setProjectSteps(Set.of(new ProjectStep("test1", 2)));
TaskGroup taskGroupSaved = TaskGroup.CreateNewTaskGroup(projectById.getDescription(), Set.of(Task.createNewTask("test1", LocalDateTime.now())));
GroupReadModel expectedResult = new GroupReadModel(taskGroupSaved);
expectedResult.setDeadline(expectedResult.getDeadline().plusDays(2));
Mockito.when(configurationProperties.getTemplate()).thenReturn(template);
Mockito.when(template.isAllowMultipleTasks()).thenReturn(true);
Mockito.when(taskGroupRepository.existsByDoneIsFalseAndProject_Id(projectId)).thenReturn(false);
Mockito.when(projectRepository.findById(projectId)).thenReturn(Optional.of(projectById));
//when
GroupReadModel result = projectService.createGroup(deadline, projectId);
//then
assertEquals(expectedResult, result);
}
My problem is that
Mockito.when(projectRepository.findById(projectId)).thenReturn(Optional.of(projectById));
resulting java.util.NoSuchElementException: No project with ID 99 found
like it was never mocked. What is interesting to me, this will work for below:
Project projectById = projectRepository.findById(projectId)
.orElseThrow(() -> new NoSuchElementException(String.format("No project with ID %d found", projectId)));
TaskGroup taskGroup = new TaskGroup();
taskGroup.setDescription(projectById.getDescription());
taskGroup.setTasks(projectById.getProjectSteps().stream()
.map(step -> Task.createNewTask(step.getDescription(), deadline.plusDays(step.getDaysToDeadline())))
.collect(Collectors.toSet()));
taskGroup.setProject(projectById);
taskGroupRepository.save(taskGroup);
As you can see, first I'm getting my object from repository, then the rest of logic takes place. However I wonder what do I do wrong so it will not work with mapping
MapResult result = projectRepository.findById(projectId)
.map(some_logic)
.orElseThrow(some_exception)
Please advise what do I do wrong, an how can I correct that?
You returned null from the .map() call, thus you fall into orElseThrow().
null comes from return taskGroupRepository.save(taskGroup);
The repository is a mock, save is not stubbed, thus null is returned.
On top if that:
You dont need to mock everything
If any of your objects are POJOs, construct them with desired state instead of mocking.
Simplify test setup
#SpringBootTest may be an overkill for your test. #MockitoExtension, #Mock and #InjectMocks should be enough.

thenThrow() not throwing an exception

I have a method in OneServiceImpl class as follows. In that class I am calling an interface method from another class.
public class OneServiceImpl {
//created dependency
final private SecondService secondService;
public void sendMessage(){
secondService.validateAndSend(5)
}
}
public interface SecondService() {
public Status validateAndSend(int length);
}
public class SecondServiceImpl {
#Override
public Status ValidateAndSend(int length) {
if(length < 5) {
  throw new BadRequestException("error", "error");
}
}
}
Now when I am try to perform unit test on OneServiceImpl I am not able to throw a BadRequestException.
when(secondService.validateAndSend(6)).thenThrow(BadRequestException.class);
Not quite sure what your use case is, but I think you should write an own test to accept and test an exception.
#Test(expected = BadRequestException.class)
public void testValidateAndSend(){
SecondService secondService = new SecondService();
secondservice.ValidateAndSend(6); //method should be lowercase
}
Not sure this is the case considering you didn't post a full example of code + unit tests, but your mock will throw only when you are passing 6 as parameter. When configuring the behaviour of your mock with when you are telling it to throw only when the validateAndSend method is called with parameter 6.
when(secondService.validateAndSend(6)).thenThrow(...)
In your code you have 5 hardcoded. So that mock will never throw for the code you have, because it's configured to react to an invocation with parameter 6 but the actual code is always invoking it passing 5.
public void sendMessage(){
secondService.validateAndSend(5)
}
If the value passed to the mock is not important you could do something like the following, that will throw no matter what's passed to it:
when(secondService.validateAndSend(any())).thenThrow(BadRequestException.class);
On the other hand, if the value is important and it has to be 5 you could change the configuration of your mock with:
when(secondService.validateAndSend(5)).thenThrow(BadRequestException.class)

Mockito mock is not properly matching arguments (?)

I'm trying to stub one method in service layer for testing another object:
#SpringBootTest
#RunWith(JUnitPlatform.class)
class WorkreportCrudFacadeTest {
private static Logger LOGGER = LogManager.getLogger(WorkreportCrudFacadeTest.class);
#Test
public void detailTest() {
final AccessRightsService ars = Mockito.mock(AccessRightsService.class);
final SystemPriceSettingService spss = Mockito.mock(SystemPriceSettingService.class);
final WorkreportActivityRepository wrar = Mockito.mock(WorkreportActivityRepository.class);
final WorkreportRepository wrr = Mockito.mock(WorkreportRepository.class);
final DomainObjectTools dot = Mockito.mock(DomainObjectTools.class);
final ApplicationEventPublisher aep = Mockito.mock(ApplicationEventPublisher.class);
Mockito.when(ars.hasEmployeeRightsToWorkReport(
ArgumentMatchers.any(Employee.class), ArgumentMatchers.any(Workreport.class)
)
).thenReturn(true);
final WorkreportCrudFacade s = new WorkreportCrudFacade(ars, spss, wrar, wrr, dot, aep);
final EmployeeId employeeId = new EmployeeId(154149756298300L);
final WorkreportId workreportId = new WorkreportId(154149757395700L);
final Workreport detail = s.detail(workreportId, employeeId);
LOGGER.debug("Detail: {}", detail);
}
}
and method that invokes tested method:
public Workreport detail(final WorkreportId workreportId, final EmployeeId employeeId) {
final Workreport workreport = domainObjectTools.getWorkreportOrThrowNotFoundException(workreportId);
final Employee viewer = domainObjectTools.getEmployeeOrThrowNotFoundException(employeeId);
boolean hasRights = accessRightsService.hasEmployeeRightsToWorkReport(viewer, workreport);
LOGGER.debug("Has rights: {}", hasRights);
if (!hasRights) {
throw new ForbiddenException();
}
return workreport;
}
but when I call tested method hasEmployeeAccessToWorkReport on WorkreportCrudFacade instance, the method is not properly stubbed (it should return true, but returns false).
I'm sure it'll be some detail but I'm not able to find out what is wrong - probably something in argument matcher, but not sure.
I'm using Mockito 2.22.0.
Citing from ArgumentMatchers javadoc:
Since Mockito any(Class) and anyInt family matchers perform a type check, thus they won't match null arguments. Instead use the isNull matcher.
I think that the following happens here: Your DomainObjectTools is an empty mock (not stubbed) and thus it returns null Workreport and null Employee. It results in calling accessRightsService.hasEmployeeRightsToWorkReport(null,null). The null values are not matched by ArgumentMatchers.any(Class).

Mockito given().willReturn() returns sporadic result

I am testing a simple logic using mockito-all 1.10.19 and spring-boot-starter-parent 2.0.4.RELEASE. I have a service, which determines whether the uploaded file has the same store codes or not. If it has, IllegalArgumentException is been thrown:
public class SomeService {
private final CutoffRepository cutoffRepository;
private final Parser<Cutoff> cutoffParser;
public void saveCutoff(MultipartFile file) throws IOException {
List<Cutoff> cutoffList = cutoffParser.parse(file.getInputStream());
boolean duplicateStoreFlag = cutoffList
.stream()
.collect(Collectors
.groupingBy(Cutoff::getStoreCode, Collectors.counting()))
.values()
.stream()
.anyMatch(quantity -> quantity > 1);
if (duplicateStoreFlag) {
throw new IllegalArgumentException("There are more than one line corresponding to the same store");
}
//Some saving logic is here
}
}
I mock up cutoffParser.parse() so, that it returns ArrayList<CutOff> with two elements within it:
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeServiceTest {
#Mock
private CutoffRepository cutoffRepository;
#Mock
private Parser<Cutoff> cutoffParser;
#InjectMocks
private SomeService someService;
#Test(expected = IllegalArgumentException.class)
public void saveCutoffCurruptedTest() throws Exception {
Cutoff cutoff1 = new Cutoff();
cutoff1.setStoreCode(1);
Cutoff cutoff2 = new Cutoff();
//corruption is here: the same storeCode
cutoff2.setStoreCode(1);
List<Cutoff> cutoffList = new ArrayList<>();
cutoffList.add(cutoff1);
cutoffList.add(cutoff2);
MockMultipartFile mockMultipartFile = new MockMultipartFile("file.csv", "file".getBytes());
//here what I expect to mock up a response with the list
given(cutoffParser.parse(any())).willReturn(cutoffList);
someService.saveCutoff(mockMultipartFile);
}
}
But the behavior I encounter is sporadic. The test is passed from time to time. During debugging I sometimes get list of size 2, sometimes get list of size 0. What is the reason of such an unpredictable behavior?
I am definitely missing something. Any help is highly appreciated.
P.S. the same situation using IntelliJ Idea and Ubuntu terminal.
Supposedly, the reason is pointed out here https://github.com/mockito/mockito/issues/1066. #InjectMocks and #Mock<...> cause test to fail occasionally.

Test Observable.FlatMap Mockito

I've been looking on internet but haven't found the solution if any (new on UnitTest and Mockito)
It's possible to test a method that return a call of a service and manipulate it's result before to return it? Example;
public Observable<Reports> getUserReports(Integer userId) {
return serviceClient
.getReports(userId)
.flatMap(serviceReports -> {
System.out.println("Testing inside flatMap"); <- never reach this line therefore duno if methods down here are invoked and work perfectly
final Observable<List<Report>> reports = getPendingReports(userId, serviceReports);
//More methods that modify/update information
return zip(observable1, observable2, observable3
(report1, report2, report3) -> {
updateReports(otherArguments, report1, report2, report3);
return serviceReports;
});
});
}
So far I've tried;
#Test
public void myTest(){
when(serviceClient
.getReports(anyInt()))
.thenReturn(Observable.just(reports));
Observable<Reports> result = mocketClass.getUserReports(userId)
}
Tryed with Spy and Mock but no luck so far. Any hint or help would be great.
To mock getReports() behavior you need to mock the serviceClient firstly and pass it into your service class.
Just as example:
#Test
public void myTest(){
// Given
final ServiceClient mockedServiceClient = Mockito.mock(ServiceClient.class);
when(mockedServiceClient
.getReports(anyInt()))
.thenReturn(Observable.just(reports));
// and create an instance of your class under testing with injected mocked service client.
final MyUserService userService = new MyUserService();
userService.setServiceClient(mockedServiceClient);
// When run a method under test
Observable<Reports> actualResult = userService.getUserReports(userId)
// Then
// actualResult to be verified.
}

Resources