Mockito with Junit for Spring - spring-boot

I am new to mockito, I need to write a JUnit test case for my project for both success and failure scenarios. I have 2 issues. First, in Mockito when I am using service.addSeasons() it is always giving as null. How should I get the response to make it assert? Second, what should I do to test failure with test exception as well? I am doing it like below but not sure whether it is correct or not. I am using jupiter.api.Test to run these.
Actual code :
public Mono<SuccessResponse> addSeason(String uuid, HuntsDetailSeasonsDto huntsDetailSeasonsDto) {
Flux<Hunter> hm = a.findByUuidAndIsPrimaryAndDeleted(uuid, true, false);
return hm.next().flatMap(h -> {
huntsDetailSeasonsDto.setHunterId(h.getId());
Mono<HuntsDetail> hsm = b.findByIdAndDeleted(huntsDetailSeasonsDto.getHuntId(), false);
return hsm.flatMap(hsm1 -> {
return b
.save(c.createAddSeasonsEntity(huntsDetailSeasonsDto, hsm1)).map(l -> {
SuccessResponse resp = new SuccessResponse();
return resp;
});
}).switchIfEmpty(Mono.error(new BusinessException()));
}).switchIfEmpty(Mono.error(new BusinessException()));
}
Mockito test case
#ExtendWith(SpringExtension.class)
#WebFluxTest
public class TestHuntsSeasonsService {
#InjectMocks
CLassAService service;
#MockBean
B b;
#MockBean
C c;
#MockBean
A a;
#MockBean
AppProperties properties;
#MockBean
SeasonsAdapter seasonsAdapter;
#Test
public void addSeasonTest() {
HuntsDetailSeasonsDto dto = new HuntsDetailSeasonsDto();
dto.setHunterId(1802L);
dto.setHuntId(7L);
dto.setSeasonsRef(24140L);
HuntsDetail huntsDetail = new HuntsDetail();
huntsDetail.setHunterId(1802L);
huntsDetail.setHuntCollectionId("7");
huntsDetail.setSeasonsDtl("24140");
Mockito.when(a.findByUuidAndIsPrimaryAndDeleted(CommonUtilTest.LOGGEDINUSER,
true, false)).thenReturn(CommonUtilTest.hunterFlux());
Mockito.when(b.findByIdAndDeleted(dto.getHuntId(), false))
.thenReturn(CommonUtilTest.huntsDetailMono());
Mockito.when(c.createAddSeasonsEntity(dto, CommonUtilTest.huntsDetail()))
.thenReturn(CommonUtilTest.huntsDetail());
Mockito.when(repository.save(huntsDetail)).thenReturn(Mono.just(huntsDetail));
service.addSeason(CommonUtilTest.LOGGEDINUSER, dto);
Mockito.verify(service, times(1)).addSeason(CommonUtilTest.LOGGEDINUSER, dto);
}
}

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.

Error when mocking a repository in unit test of a spring webflux application (using JUnit and Mockito)?

I am yet to find a solution to this.
My Test class:
#WebFluxTest(controllers = {PatronController.class})
#Import({PatronService.class}) //to satisfy PatronController dependency.
#ExtendWith({PatronParameterResolver.class})
class PatronFunctionsSpec {
private Patron patron;
private Mono<Patron> patronMono;
private final PatronService patronService = Mockito.mock(PatronService.class);
#MockBean
private PatronRepository patronRepository;
#Autowired
private WebTestClient client;
#BeforeEach
void init(Patron injectedPatron) {
patron = injectedPatron;
patronMono = Mono.just(patron);
}
//Patron Story: patron wants to create an account with us
#Nested
#DisplayName("Creating a patron.")
class CreatingPatron {
#Test
#DisplayName("PatronService.create() returns success msg in Response obj after creating patron.")
void getResponseObjFromServiceCreate() {
Flux<Patron> patronFlux = Flux.from(patronMono);
Mockito.when(patronRepository.saveAll(patronMono)).thenReturn(patronFlux);
PatronService patronService = new PatronService(patronRepository);
Mono<Response> responseStream = patronService.create(Mono.just(patron));
Mono<Response> expectedResponseStream = Mono.just(new Response("Welcome, patron. Can't show emojis yet -- sorry."));
assertEquals(expectedResponseStream, responseStream);
}
}
}
See my PatronService class with its code:
#Service
public class PatronService {
private final PatronRepository patronRepository;
public PatronService(PatronRepository patronRepository) {
this.patronRepository = patronRepository;
}
/**
*
* persists patron via PatronRepo
*/
public Mono<Response> create(Mono<Patron> patronMono) {
patronRepository.saveAll(patronMono).subscribe();
return Mono.just(new Response("Welcome, patron. Can't find the emojis yet -- sorry."));
}
}
I am testing the PatronService's create(), so need to mock and stub PatronRepository and its function respectively. But the problem is: after running the test case, I get this exception:
java.lang.NullPointerException: Cannot invoke "reactor.core.publisher.Flux.subscribe()" because "patronFlux" is null
at com.budgit.service.PatronService.create(PatronService.java:26)
How can I fix this?

Errors: UnfinishedStubbing

I am writing Junit test case and I want to mock KafkaTemplate method kafkaTemplate.send(TOPIC_NAME, "someData");. In my project, I am using spring boot and Kafka.
Below is the StudentRecords class. I am using mockito for mocking the dependencies.
#Component
public class StudentRecords {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#Value("${topicNameForStudents}")
private String TOPIC_NAME;
public String sendStudentData(StudentDTO studentDTO) {
String studentStr = null;
try {
if(null == studentDTO) {
throw new StudentException("studentDTO Object cant be null");
}
if(studentDTO.getId() == null) {
throw new StudentException("Id cant be empty");
}
ObjectMapper mapper = new ObjectMapper();
studentStr = mapper.writeValueAsString(srvgExecution);
kafkaTemplate.send(TOPIC_NAME, studentStr);
return "SUCCESS";
} catch (JsonProcessingException e) {
e.printStackTrace();
return "ERROR";
}
}
}
And test class is as follows:
#ExtendWith({ SpringExtension.class, MockitoExtension.class })
class StudentRecordsTest {
#InjectMocks
StudentRecords studentRec;
#Mock
private KafkaTemplate<String, String> kafkaTemplate;
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
}
}
And I getting the following error
[ERROR] Errors:
[ERROR] studentRec.testSendStudentData: ยป UnfinishedStubbing
It is happening at line studentRec.sendStudentData(studentDTO);
How I can resolve/write the junit for this?
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
}
after updating the junit to above one, ended up with below error at this statement Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
Argument(s) are different! Wanted:
kafkaTemplate.send(
<any string>,
<any string>
);
Your mock statement is incomplete.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
KafkaTemplate's send method returns a ListenableFuture object and hence you need to have your mock return it.
I understand, you are not really using the returned value as part of your code.
In that case you may simply return null as below.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
Although, I would suggest you should ideally check for return value for better error handling, but that can be a different story altogether.
In case you do plan to handle error by checking the return value, your above mock statement can be written to return both success and fail cases.
You may check below thread for more details on how to set the correct mock expectations for KafkaTemplate.
How to mock result from KafkaTemplate

spring boot restcontroller test returns null

I'm testing the "find" method of the controler that returns a "findById" but the return is always null.
My project is structured as follows:
I have a LegalPerson entity
A repository that extends a JpaRepository
And a service that "uses" the repository.
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension.class)
class LegalPersonResourceTest {
#MockBean
private LegalPersonService service;
#Autowired
private MockMvc mvc;
#Test
void find() {
var localDate = LocalDate.of(1955, 10, 25);
List<Long> subsidiaries = new ArrayList<>() {{
add(10L);
add(20L);
}};
List<Long> phones = new ArrayList<>() {{
add(50L);
add(60L);
}};
var mockLP = LegalPerson.builder()
.id(1L)
.active(true)
.companyId(1L)
.tradeName("Test Company Trade Name")
.companyName("Test Company Company Name")
.email("test#com")
.cnpj("testCNPJ")
.stateRegistration("test state Registration")
.municipalRegistration("test Municipal Resgistration")
.openingDate(localDate)
.address(1L)
.companyType(CompanyEnum.HEADOFFICE)
.subsidiaries(subsidiaries)
.phones(phones)
.build();
Mockito.doReturn(mockLP).when(service).find(1L);
}
}
I wonder what I'm forgetting, or writing wrong.
EDITED 01 :
Mockito.when(this.service.find(ArgumentMatchers.eq(1L))).thenReturn(mockLP);
mvc.perform(MockMvcRequestBuilders.get("/api/clients/lp/{id}", 1L))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType("application/json;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.jsonPath("$.active", Matchers.is(true)));
It works perfectly.
But if I add
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.ETAG, "\"1\""))
return null.
You only are mocking the service, but no testing anything in this code, you may want to test the controller, something like this:
import static org.mockito.BDDMockito.given;
#Test
public void shouldGetAPerson() throws Exception {
//...
given(service.find(1L)).willReturn(mockLP);
mvc.perform(MockMvcRequestBuilders.get("/person/1")
.contentType("application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", Matchers.containsString("1")));
}
Try with ArgumentMatchers
Mockito.when(this.service.find(ArgumentMatchers.eq(1L)).thenReturn(mockLP);

Testing Spring Retry Circuit Breaker

I am using Spring Retry Circuit Breaker in one of my Spring Boot applications as:
#CircuitBreaker(include = CustomException.class, maxAttempts = 3, openTimeout = 2000L, resetTimeout = 4000L)
StudentResponse getStudentInfo(String studentId) {
StudentResponse res = studentRepository.getInfoByStudentId(studentId);
return res;
}
#Recover
StudentResponse recoverStudentInfo(CustomException ex, String studentId) {
throw new StudentApiException("In recovery...");
}
Now I'm trying to test it using Junit 5 as:
#ExtendWith(SpringExtension.class)
#SpringBootTest
#EnableRetry
public class StudentServiceTest {
#Autowired
StudentService studentService;
#MockBean
StudentRepository studentRepository;
#Test
public void testCircuitBreakGetStudentInfo(){
String id = "id";
doThrow(CustomException.class).when(studentRepository).getInfoByStudentId(id);
StudentResponse res = this.studentService.getStudentInfo(id);
verify(studentRepository, times(3)).getInfoByStudentId(id); // this is telling that there has been 1 interaction with the mock
}
}
Then I tried:
#Test
public void testCircuitBreakGetStudentInfo(){
String id = "id";
doThrow(CustomException.class).when(studentRepository).getInfoByStudentId(id);
assertThrows(StudentApiException.class,
()->{ this.studentService.getStudentInfo(id); }); // this is also telling that there has been 1 interaction with the mock instead of 3
}
Shouldn't there be 3 interactions? Or will it be one? Any explanation will be of great help. Any other tips on testing the circuit breaker is appreciated (I could hardly find any example on testing this circuit breaker). Thanks

Resources