Home > front end >  Mockito how to mock Optional.map().orElseThrow()
Mockito how to mock Optional.map().orElseThrow()

Time:02-03

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?

CodePudding user response:

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.

  • Related