Test Observable.FlatMap Mockito - java-8

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.
}

Related

How to test findById method?

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().

How to mock controller parameter method with Mockito?

I have the following controller method where criterias is an object build with query parameters :
#GetMapping
public List<Employee> findAll(CustomCriteria criterias) {
// this method build a custom mongoDB query object
final Query query = criterias.buildMongoDBQueryFromCriteria();
return employeeService.find(query);
}
The test is written as follow :
#Test
void get_all_employees_with_criterias() {
given(employeeService.find(any(Query.class)))
.willReturn(List.of(new Employee(), new Employee));
final var responseBody = mvc.perform(get("/employees?companyId=12,14")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();
assertThatJson(responseBody).isArray().size().isEqualTo(2);
}
I can't find how to mock criterias.buildMongoDBQueryFromCriteria(). Indeed, there are a lot of logic inside this method, and I don't want it to be called for real with #WebMvcTest.
I have already tried to use #Spy annotation in the controller test class but it doesn't seems to work :/
I'm pretty sure that it must be really basic, but I didn't find any equivalent needs over Google.
Thanks for your help
EDIT
Based on #nnhthuan response I updated my test as follow, but it still doesn't work:
#Test
void get_all_employees_with_criterias() {
var customCriteriaMock = Mockito.mock(CustomCriteria.class);
var mockedQuery = Mockito.mock(Query.class);
when(customCriteriaMock.buildMongoDBQueryFromCriteria()).thenReturn(mockedQuery);
given(employeeService.find(mockedQuery))
.willReturn(List.of(new Employee(), new Employee()));
final var responseBody = mvc.perform(get("/employees?companyId=12,14")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();
assertThatJson(responseBody).isArray().size().isEqualTo(2);
}
If you're asking how to write unit test, and this is how, not what you're trying to write above.
#Test
public void testFindAll() {
CustomCriteria mockCriteria = Mockito.mock(CustomCriteria.class);
Query mockQuery = Mockito.mock(Query.class);
List<Employee> expectation = new ArrayList<>();
when(mockCriteria.buildMongoDBQueryFromCriteria()).thenReturn(mockQuery);
when(employeeService.find(mockQuery)).thenReturn(expectaion)
List<Employee> actual = controller.findAll(mockCriteria);
assertThat(actual).sameInstance(expectation); // Or whatever framework you are using to assert.
}
If you're asking how to write integration test with your real service, so do not use mocking framework. :)

Unit testing GatewayFilter causes NullPointerException

I'm trying to unit test my GatewayFilter, however I'm having troubles running even simple test.
This is small example of what is failing right now
#ExtendWith(MockitoExtension.class)
public class SomeFilterTest {
private final GatewayFilter gatewayFilter = (exchange, chain) ->
Mono.just("Hello")
.flatMap(this::doSomething)
.switchIfEmpty(Mono.defer(() -> chain.filter(exchange)));
private Mono<Void> doSomething(String value) {
System.out.println(value);
return Mono.empty();
}
#Test
void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(GatewayFilterChain.class);
gatewayFilter.filter(exchange, chain).block();
}
}
Unfortunatelly, it is failing because of
The Mono returned by the supplier is null
java.lang.NullPointerException: The Mono returned by the supplier is
null at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) at
reactor.core.publisher.Mono.subscribe(Mono.java:4361)
And to be honest, I have no idea why is that happening?
You have not stubbed out the filter method call on your mock object, GatewayFilterChain. As a result, the supplier () -> chain.filter(exchange) returns null. You are not allowed to create a Mono with a value of null, hence the exception.
As a result your test should look something like
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
// stubbing behaviour on our mock object
given(chain.filter(exchange)).willReturn(Mono.empty());
gatewayFilter.filter(exchange, chain).block();
}
Additionally, I would suggest using StepVerifier instead of using block() in unit tests. This is provided by reactor-test and is purpose built for unit testing reactive code
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
given(chain.filter(exchange)).willReturn(Mono.empty());
StepVerifier.create(gatewayFilter.filter(exchange, chain))
.verifyComplete();
}
Here is a very useful Step Verifier Tutorial to help you get started

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)

org.junit.ComparisonFailure: expected:<com.test.entity.VendorEntity#3e60be48> but was:<null>

I am running Junit test case for service layer but I am getting
org.junit.ComparisonFailure: expected:VendorEntity#3e60be48 but was:null
When vendorRepo.save(vendorEntity) method is called it returns null, I am not able to figure out why it is returning null. Below is my code.
#Autowired
private VendorSvc vendorSvc;
#MockBean
private VendorRepo vendorRepo;
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntiy = getVendor();
Mockito.when(vendorRepo.save(vendorEntiy)).thenReturn(vendorEntiy);
// saveVendorForm return null
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(getVendorDto());
assertThat(vendorEntity2).isEqualTo(vendorEntiy);
}
After making some change in saveVendorForm which accept vendorEntity below code works but I don't want to pass entity class object to service layer as I want to create entity object in service layer and pass it to dao layer
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntity = getVendor();
Mockito.when(vendorRepo.save(vendorEntity)).thenReturn(vendorEntity);
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(vendorEntity);
assertThat(vendorEntity2).isEqualTo(vendorEntity);
}
private VendorEntity getVendor() {
VendorEntity vendorEntity = new VendorEntity();
SocietyEntity societyEntity = new SocietyEntity();
societyEntity.setSocietyId(1L);
PincodeEntity pincodeEntity = new PincodeEntity();
pincodeEntity.setPincodeId(1L);
vendorEntity.setVendor("XYZ Cafe");
vendorEntity.setAddress("abc address");
vendorEntity.setEmailId("xyz#gmail.com");
vendorEntity.setContactNo1("123456");
vendorEntity.setContactNo2("123457");
vendorEntity.setSocietyId(societyEntity.getSocietyId());
vendorEntity.setPincodeId(pincodeEntity.getPincodeId());
vendorEntity.setWebsite("www.xyzabc.com");
vendorEntity.setCategoryId(2);
vendorEntity.setStatus(Constant.ACTIVE);
vendorEntity.setCreatedBy(1L);
vendorEntity.setCreatedDate(CommonUtil.getCurrentTimeStamp());
vendorEntity.setCreatedIp(Constant.DEFAULT_IP);
vendorEntity.setSocietyEntity(new SocietyEntity());
vendorEntity.setPincodeEntity(new PincodeEntity());
return vendorEntity;
}
#Override
public VendorEntity saveVendorForm(VendorDto vendorDto) {
VendorEntity vendorEntity = new VendorEntity();
// copy properties from (source,target)
BeanUtils.copyProperties(vendorDto,vendorEntity);
vendorEntity.setCreatedBy(vendorDto.getCreatedBy());
vendorEntity.setCreatedDate(vendorDto.getCreatedDate());
vendorEntity.setCreatedIp(vendorDto.getCreatedIp());
vendorEntity.setModifiedBy(vendorDto.getModifiedBy());
vendorEntity.setModifiedDate(vendorDto.getModifiedDate());
vendorEntity.setModifiedIp(vendorDto.getModifiedIp());
vendorEntity.setSocietyEntity(new SocietyEntity());
vendorEntity.setPincodeEntity(new PincodeEntity());
vendorEntity.setStatus(Constant.ACTIVE);
// below code returns null but works well when run in tomcat and form submitted through web browser
return vendorRepo.save(vendorEntity);
}
public interface VendorRepo extends JpaRepository<VendorEntity, Long> {
}
Can someone please tell me what is wrong in the code.
You are mocking save method for object vendorEntity but actaully passing a different object created via VendorDto object. Both are different object I guess which causing null in return.
Follow my comment on your test case(did not make any change except comments).
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntiy = getVendor();
//Mocking the verndorRepo.save to return vendorEntiy when save is called with vendorEntiy
Mockito.when(vendorRepo.save(vendorEntiy)).thenReturn(vendorEntiy);
// saveVendorForm return null
// Actually passed a different object which may not be equal to vendorEntiy
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(getVendorDto());
assertThat(vendorEntity2).isEqualTo(vendorEntiy);
}
saveVendorForm may not be generating the exact VendorEntity object that we configured in mocking.
So if you make sure that getVendorDto() to VendorEntity trasnformation generates object similar to vendorEntity(the one which is getting created via getVendor method) then your test case would work as expected.
Similar objects means equals method should return true for given objects.

Resources