How to mock controller parameter method with Mockito? - spring-boot

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

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

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

mockMvc jUnit testing with #SortDefault and #PageableDefault

I am trying to test a pagination method which also sorts the content by created date. The method does work how I expect it, however I an unsure how I can correctly test it. Have found no working solution for my problem even the
I know that the issue is caused by #SortDefault being present as when I remove it the problem is solved, could work around it by creating an sql repository call which will sort it asc or desc.
Furthermore the error I currently receive is java.lang.AssertionError: Content type not set
tried adding .setCustomArgumentResolvers(new SortHandlerMethodArgumentResolver()).build(); but without any luck also tried other methods of .andExpect(content().contentTypeCompatibleWith("application/json")) also no luck. Still complains that content type is not set.
Below you can see my controller method
#GetMapping("/stuff")
public ResponseEntity<Page<Stuff>> findAllStuffs(#PageableDefault(value = 20) #SortDefault(sort = "created", direction = Sort.Direction.DESC)Pageable pageable){
log.info("Start of findAllStuffs method");
Page<Stuff> thePage=repository.findAll(pageable);
if(thePage==null )
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
else
return new ResponseEntity<>(thePage,HttpStatus.OK);
}
Below you can see my test controller method
#Test
public void findAllStuffs() throws Exception {
PageRequest pageRequest = new PageRequest(0,20);
Page<Stuff> pages = new PageImpl<>(stuffs);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new SortHandlerMethodArgumentResolver()).build();
when(repository.findAll(pageRequest)).thenReturn(pages);
this.mockMvc.perform(get("/api/stuffs")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", Matchers.hasSize(4)))
.andExpect(status().isOk());
}
Any help or suggestions will be truly appreciated.

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

How to write test code for Web API in Visual Studio?

I'm a bit new to test project. I currently have a web api project which contains Get, Put, Post and Delete methods. When comes to writing test cases, I'm confused. Should I write test code to test the Http URL?
My web api code:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
using (var unitOfWork = new UnitOfWork(_db))
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
}
And in my test project, I kind of stuck here. We are using Xunit. How to write test code to test the Get method? Or should I write code to test the URL api/values/5 instead, but how?
[Fact]
public void GetTest()
{
using (var unitOfWork = new UnitOfWork(new MockDatabase()))
{
}
}
Any help would be appreciated.
You need to make a couple of changes before you can really unit test your controller. You need to pass an instance of your UnitOfWork class into the controller in its constructor. Then your controller method code becomes:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
Then in your unit tests you do this:
[Fact]
public void GetTest()
{
// Arrange
// You really want to mock your unit of work so you can determine
// what you are going to send back
var unitOfWork = new MockUnitOfWork();
var systemUnderTest = new Controller(unitOfWork);
system.Request = new HttpRequestMessage();
// Act
var result = systemUnderTest.Get(1);
// Assert
// Here you need to verify that you got back the expected result
}
Injecting the UnitOfWork class into the controller is probably another question. Mark Seemann has an excellent post on the subject, but it might be a little advanced. There are a number of different ways to accomplish that with simpler (but maybe not as robust methods). Google is your friend with that. But if you have questions, post another question.
Hope that helps.
You would need to make some design changes to your controller to make it easy to test. In your action you are creating an instances which will make it difficult to test with a fake dependencies to the controller. Also your controller should depend on abstractions rather than concretions which will allow the controller to be more testable.
public class MyWebApiController : ApiController {
private IUnitOfWork unitOfWork;
public MyWebApiController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
//...other code
}
Notice the controller uses dependency injection to inject an IUnitOfWork. That makes the controller more testable, because you can inject mocks of its dependencies when unit testing.
From there it is just to create an instance of the controller and call the method under test with mocks of the dependencies.
[Fact]
public void GetTest() {
//Arrange (Setup the parts needed to run test)
var unitOfWork = new MockUnitOfWork(new MockDatabase());
//Or using your mocking framework of choice
//var unitOfWork = Mock.Of<IUnitOfWork>(); //this is using Moq
var controller = new MyWebApiController(unitOfWork);
var id = "Test Id value here";
//Act (call the method under test)
var result - controller.Get(id);
//Assert (check results)
//...Do your assertion pertaining to result of calling method under test
}
Reference : Unit Testing Controllers in ASP.NET Web API 2

Resources