Mock error UnfinishedStubbingException in a Junit test - java

I've created a test to try out my microservice. A simple get call that through 3 parameters returns me the message with the specifications of that user. I created it using Junit and Mockito. Below:
#Test
public void TestOk() throws Exception{
CarsRequest carsRequest = new CarsRequest();
carsRequest.setName(TestCostants.NAME);
carsRequest.setPlate(TestCostants.PLATE);
carsRequest.setPrice(TestCostants.PRICE);
Cars car = new Cars("BMW","TG35647", "15000","re",80000000,
null,null);
List<Cars> cars = new ArrayList<>();
cars.add(car);
Mockito.when(
service.getByPlate(carsRequest.getName(), carsRequest.getPlate(),
carsRequest.getPrice())).thenReturn(cars);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/resources/cars").accept(
MediaType.APPLICATION_JSON).header("Authorization","Basic //myAuth");
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "{\"name\"=\"BMW\", \"plate\"=\"TG35647\", " +
"\"price\"=\"15000\",\"brand\"=\"re\", \"kilometers\"=\"80000000\", \"revisiondate\"=\"null\", \"owner\"=\"null\"}";
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
});
clearly the constants of the variables that are passed conform to those of the message that must return me. When I try to start the test it gives me an error
This is the stacktrace I am reporting:
2021-03-29 12:45:07.906 [main] INFO o.s.s.l.SpringSecurityLdapTemplate - Ignoring PartialResultException
{
"#timestamp" : "2021-03-29T12:45:08.119+02:00",
"#version" : "1",
"message" : "[GET] http://localhost/resources/cars/ call resulted in the following error: Content type '' not supported",
"logger_name" : "my.project.car.controller.ExceptionHandlingController",
"thread_name" : "main",
"level" : "ERROR",
"level_value" : 40000
}
ErrorResponse: org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported
org.springframework.mock.web.MockHttpServletResponse#135f160e
MockHttpServletRequest:
HTTP Method = GET
Request URI = /resources/cars/
Parameters = {}
Headers = [Accept:"application/json", Authorization:"Basic //myAuth"]
Body = null
Session Attrs = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = [Content-Type:"application/json", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = application/json
Body = {"esito":"KO","codiceEsito":"ERROR","descrizioneEsito":"Errore generico"}
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError:
Expected: name
but none found
;
Expected: plate
but none found
;
Expected: price
but none found
;
Expected: brand
but none found
;
Expected: kilometers
but none found
;
Expected: revisiondate
but none found
;
Expected: owner
but none found
;
Trying to go to Debug, I get this error back, but I don't know how to handle it at all
Method threw 'org.mockito.exceptions.misusing.UnfinishedStubbingException' exception. Cannot evaluate my.project.service.CarService$MockitoMock$1824138024.toString()
I guess the two errors are related but I don't know how to fix this.
EDIT:
As requested, I add my Car Service:
#Service
public class CarService {
#Autowired
CarRepository carRepository;
#Autowired
ObjectMapper objectMapper;
public List<Car> getByPlate(String name, String plate, String price) throws JsonProcessingException {
List<Car> car = new ArrayList<>();
for (Cache.Entry<String, Car> entry: carRepository.findAllByNameAndPlateAndPrice(name, plate,price)){
car.add(new Car(entry.getKey(), entry.getValue()));
System.out.println("Entry: " + objectMapper.writeValueAsString(entry));
}
return cars;
}
}
and my CarController
#RestController
#RequestMapping("/resources/")
public class CarController {
#Autowired
CarService carService;
#Autowired
ObjectMapper objectMapper;
#GetMapping(
value = "/cars",
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public CarsResponse getCars(#RequestBody CarsRequest request) throws IOException {
//some code
}

First of all, using GET requests with a body is the poor practice. Use POST requests instead.
Then, spring tells you about org.springframework.web.HttpMediaTypeNotSupportedException and in your controller method getCars() you describe the next:
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE}
But in your test you don't specify any content-type:
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/resources/cars").accept(
MediaType.APPLICATION_JSON).header("Authorization","Basic //myAuth");
Try to add content-type .contentType(MediaType.APPLICATION_JSON):
RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/resources/cars")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Basic //myAuth");
Also, I don't see how you pass the body to the request. Try to add it .content(mapper.writeValueAsString(carsRequest)):
RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/resources/cars")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Basic //myAuth")
.content(mapper.writeValueAsString(carsRequest));
where mapper is ObjectMapper. I assume you are using com.fasterxml.jackson.databind.ObjectMapper.

Related

I have an error "Content type not set" when testing with junit

I use spring boot 2.7 and test with junit
during the execution of the tests I get an error :
the other tests work well.
controller
#PostMapping(value = "/employees")
public ResponseEntity<Employee> addEmployee(#Valid #RequestBody EmployeeDto employeeDto) {
Optional<Employee> employeeDb = employeeService.findByEmail(employeeDto.getEmail());
// must not exist in database
if (!employeeDb.isEmpty()) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
// convertion dto -> model
Employee employee = employeeDto.toEmployee();
return new ResponseEntity<>(employeeService.save(employee), HttpStatus.OK);
}
controllerTest
#Test
void addEmployee() throws Exception {
when(employeeService.save(employeeDto.toEmployee())).thenReturn(employee);
when(employeeService.findByEmail(any(String.class))).thenReturn(Optional.of(employee));
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(employeeDto);
mockMvc.perform(
MockMvcRequestBuilders
.post(REST_URL)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.characterEncoding("utf-8")
.content(json)
)
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.firstname", is(employee.getFirstname())))
.andExpect(jsonPath("$.lastname", is(employee.getLastname())))
.andExpect(jsonPath("$.email", is(employee.getEmail())));
}
the error : "Content type not set"
yet in the test I indicate well the content-type : ".contentType(MediaType.APPLICATION_JSON)"
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/employees/
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"74"]
Body = {"email":"test1#test.com","firstname":"firstname1","lastname":"lastname1"}
Session Attrs = {}
Handler:
Type = com.acme.app1.controllers.EmployeeController
Method = com.acme.app1.controllers.EmployeeController#addEmployee(EmployeeDto)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Content type not set
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:37)
at org.springframework.test.util.AssertionErrors.assertTrue(AssertionErrors.java:70)
at org.springframework.test.util.AssertionErrors.assertNotNull(AssertionErrors.java:106)
for the test to be successful, what should I change?
The content type you've set is used for the request, but in your assertion you're checking for the content type which is returned in the response.
As you can see in the log, there is no content type or body set in the MockHttpServletResponse:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
So there seems to be an issue with the returned object in your mocked result of
when(employeeService.save(employeeDto.toEmployee())).thenReturn(employee);
since the result of this will be rendered to a JSON object you're doing your assertions on.

Spring boot intergration test returns empty result

I am trying to make intergration tests for my webflux controller, but the test are failing either on not set content-type or on empty content.
Controller class:
#RestController
#RequiredArgsConstructor
#SecurityRequirement(name = "bearerAuth")
#Log4j2
public class OnboardingController {
private final Service service;
#GetMapping(value = "/organizationQuotas", produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<OrganizationQuota> getOrganizationQuotas() {
return service.getAllOrganizationQuotas();
}
}
Service class is a simple flux-returning service.
Test class:
#WebMvcTest(OnboardingController.class)
#RunWith(SpringRunner.class)
public class OnboardingControllerIT {
#MockBean
Service service;
private EasyRandom easyRandom = new EasyRandom();
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser(authorities = {"..."})
#DisplayName("Should List All organization quotas when GET request to /organizationQuotas")
public void shouldReturnOrganizationQuotas() throws Exception {
when(service.getAllOrganizationQuotas())
.thenReturn(Flux.fromStream(easyRandom.objects(OrganizationQuota.class, 5)));
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/organizationQuotas").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
// .andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.*", isA(ArrayList.class)))
.andReturn();
}
}
At this state the output looks like this:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /organizationQuotas
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json"]
Body = null
Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl#d1e9b4bb: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#d1e9b4bb:...}
Handler:
Type = controller.OnboardingController
Method = controller.OnboardingController#getOrganizationQuotas()
Async:
Async started = true
Async result = [OrganizationQuota{allowPaidServicePlans=true, ...}]
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
and it ends with exception
No value at JSON path "$.*"
java.lang.AssertionError: No value at JSON path "$.*"
...
I have these dependencies
testImplementation 'org.jeasy:easy-random-randomizers:5.0.0'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testCompile 'io.projectreactor:reactor-test'
Security should work ok. I can see the async result that is correct but my matchers are not working with it.
The content type is not returned as well. Is it because of the async character of request? How can I make it evaluate? Thanks for help.
I found it at howToDoInJava that testing async controller is different:
I had to use asyncDispatch method. Form the referenced page, here is the example:
#Test
public void testHelloWorldController() throws Exception
{
MvcResult mvcResult = mockMvc.perform(get("/testCompletableFuture"))
.andExpect(request().asyncStarted())
.andDo(MockMvcResultHandlers.log())
.andReturn();
mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith("text/plain"))
.andExpect(content().string("Hello World !!"));
}
now it works correctly.

Unit testing Spring REST API Service (Update (PUT Method))

I'm trying to unit test a service for my controller in my API but i'm getting the following error :
2020-05-20 15:23:51.493 WARN 25469 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<com.tropicalia.meu_cardapio.domain.user.User> com.tropicalia.meu_cardapio.api.user.update.UserUpdateRest.update(com.tropicalia.meu_cardapio.domain.user.User,java.lang.Long)]
MockHttpServletRequest:
HTTP Method = PUT
Request URI = /users/89
Parameters = {}
Headers = [Content-Type:"application/json"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.tropicalia.meu_cardapio.api.user.update.UserUpdateRest
Method = com.tropicalia.meu_cardapio.api.user.update.UserUpdateRest#update(User, Long)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :202
Actual :400
This is my test class :
#RunWith(SpringRunner.class)
#WebMvcTest(UserUpdateRest.class)
public class UpdateUserTest {
#Autowired
private MockMvc mvc;
#MockBean
private UserUpdateService service;
#Test
public void updateUser_whenPutUser() throws Exception {
User user = new User();
user.setName("Test Name");
user.setId(89L);
given(service.updateUser(user.getId(), user)).willReturn(user);
mvc.perform(put("/users/" + user.getId().toString())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isAccepted())
.andExpect(jsonPath("name", is(user.getName())));
}
}
And this is my service
#Service
public class UserUpdateService {
#Autowired
UserRepository repository;
public User updateUser(Long id, User user) {
repository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException("User not found."));
return repository.save(user);
}
}
Would really appreciate if someone could help me with this one.
From what i understand, there's something wrong with the request body but i have no idea what to do to fix it.
As specified in the error message, requestbody is missing.
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing
All you need to do is add body content to the unit test like this
ObjectMapper mapper = new ObjectMapper();
mvc.perform(put("/users/" + user.getId().toString())
.contentType(MediaType.APPLICATION_JSON))
.content(mapper.writeValueAsString(user))
.andExpect(status().isAccepted())
.andExpect(jsonPath("name", is(user.getName())));
you can also pass content like this
.content("{\"id\":\"89\", \"name\":\"Test Name\"}")

MockHttpServletResponse returning empty body for Pageable endpoint

I have the following test code where I'm testing a Pageable endpoint that list all entries for student.
#Autowired
private MockMvc mockMvc;
#MockBean
private StudentRepository studentRepository;
private PageableHandlerMethodArgumentResolver pageableArgumentResolver = new PageableHandlerMethodArgumentResolver();
#BeforeEach
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(new StudentEndpoint(studentRepository))
.setCustomArgumentResolvers(pageableArgumentResolver)
.build();
}
#Test
#WithMockUser(username = "xx", password = "xx", roles = "USER")
public void whenListStudentUsingCorrectStudentUsernameAndPassword_thenReturnStatusCode200 () throws Exception {
List<Student> students = asList(new Student(1L, "Legolas", "legolas#lotr.com"),
new Student(2L, "Aragorn", "aragorn#lotr.com"));
when(studentRepository.findAll()).thenReturn(students);
mockMvc.perform(get("http://localhost:8080/v1/protected/students/"))
.andExpect(status().isOk())
.andDo(print());
verify(studentRepository, times(1)).findAll();
}
The problem here is that the verify(studentRepository, times(1)).findAll(); doesn't work because MockHttpServletResponse is returning a null Body.
Thats my endpoint:
#GetMapping(path = "protected/students")
public ResponseEntity<?> listAll (Pageable pageable) {
return new ResponseEntity<>(studentDAO.findAll(pageable), HttpStatus.OK);
}
And my log:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null Redirected URL = null
Cookies = []
Argument(s) are different! Wanted:
br.com.devdojo.repository.StudentRepository#0 bean.findAll(
);
-> at br.com.devdojo.TestingTestTech.whenListStudentUsingCorrectStudentUsernameAndPassword_thenReturnStatusCode200(TestingTestTech.java:68)
Actual invocations have different arguments:
br.com.devdojo.repository.StudentRepository#0 bean.findAll(
Page request [number: 0, size 20, sort: UNSORTED]
);
Could someone please help with the right way to test pageable response? Thanks.
Finally, I found how to fix it.
You just need to pass a Pageable object as parameter to your findAll method that returns a Pageable JSON.
Thats my new working code:
Page<Student> pagedStudents = new PageImpl(students);
when(studentRepository.findAll(isA(Pageable.class))).thenReturn(pagedStudents);
mockMvc.perform(get("http://localhost:8080/v1/protected/students/"))
.andExpect(status().isOk())
.andDo(print());
verify(studentRepository).findAll(isA(Pageable.class));
And the MockHttpServletResponse:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"content":[{"id":1,"name":"Legolas","email":"legolas#lotr.com"},{"id":2,"name":"Aragorn","email":"aragorn#lotr.com"}],"pageable":"INSTANCE","totalElements":2,"totalPages":1,"last":true,"size":2,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"first":true,"numberOfElements":2,"empty":false}
Forwarded URL = null
Redirected URL = null
Cookies = []

MockMVC test with Custom Authentication Principal

I am trying to write a test for an API endpoint but can not get past the security. At least that's what I understand is happening.
From what I understood in the docs (https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/test-method.html) I think I am following the right way?
My Rest controller looks as following. I have an Authentication Principal which I believe is causing the problem.
#RestController
#RequestMapping("/api")
public class VehicleResource {
#GetMapping(value = "/status", produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<StatusResponse> getStatus(#AuthenticationPrincipal CustomUser customUser) {
StatusResponse statusResponse = vehicleService.getStatus(customUser.getVin());
LOGGER.debug("Sent statusResponse: {}", statusResponse);
return new ResponseEntity<>(statusResponse, HttpStatus.OK);
}
My test is fairly simple:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = { MobileBackendApplication.class })
#AutoConfigureMockMvc
public class VehicleResourceTest {
#Autowired
private MockMvc mockMvc;
private AccountCredentials testCredentials;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
testCredentials = new AccountCredentials("test.user#test.com", "testPassword");
}
#Test
#WithMockCustomUser
public void getStatusTest() throws Exception {
mockMvc.perform(
get("/api/status").with(httpBasic(testCredentials.getUsername(), testCredentials.getPassword())))
.andExpect(status().isOk());
}
I have a MockCustomUser interface so I can pass along custom users:
#WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public #interface WithMockCustomUser {
String username() default "test.user#bmw.com";
String password() default "testPassword";
String vin() default "123TEST456VIN789";
String pushNotificationId() default "testPushId";
}
With the WithMockCustomUserSecurityContextFactory:
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
#Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
CustomUser principal = new CustomUser(customUser.username(), customUser.password(), customUser.vin(),
new ArrayList<String>(Arrays.asList(customUser.pushNotificationId())), null,
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password",
principal.getAuthorities());
context.setAuthentication(auth);
return context;
}
}
When I run the test I receive a 401 Unauthorized instead of a 200. Other answers referred to maybe missing the Basic Authentication header, but I seem to be passing that along. Below is what is printed when I run the test.
MockHttpServletRequest:
HTTP Method = GET
Request URI = /api/status
Parameters = {}
Headers = {Authorization=[Basic dGVzdC51c2VyQHNhbXBsZS5jb206dGVzdFBhc3N3b3Jk]}
Body = null
Session Attrs = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Unauthorized
Headers = {Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[POST, GET], Access-Control-Max-Age=[3600], Access-Control-Allow-Headers=[Origin, X-Requested-With, Content-Type, Accept, Authorization], Access-Control-Expose-Headers=[Authorization], WWW-Authenticate=[Basic realm="Realm"], X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []

Categories