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.