RestController Integration Tests Junit5 Error - spring-boot

I am development a integration tests for a RestController
#RestController
public class MyController {
//fields
private ModelMapper mapper;
private MyService myService;
//constructor
public MyController(ModelMapper mapper, Myservice myService) {
this.mapper = mapper;
this.myService = myService;
}
#GetMapping(value = "/test", produces = "application/json")
public ResponseEntity<Page<Dto>> retrieveAll(pageNumber, pageSize)
.....
and my test classes implementation
#ExtendWith(SpringExtension.class)
#WebMvcTest(MyController.class)
class MyControllerIntegrationTest {
#Autowired
MockMvc mockMvc;
#MockBean
MyService myService;
#MockBean
ModelMapper modelMapper;
#Test
void test() throws Exception{
ObjFactory objFactory = new ObjFactory();
Mockito.when(myService.retrieveAll(1,20)).thenReturn(objFactory.createRandomList());
mockMvc.perform(MockMvcRequestBuilders.get("/test"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
and in my Mockito is returning a array with 2 model/entity elements.
i am getting this error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
...
at java.base/java.util.ArrayList.forEach
at java.base/java.util.ArrayList.forEach
Caused by: java.lang.NullPointerException
at com.testing.john.controller.myController.retrieveAll(MyController.java:48)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
What may be the problem?
This endpoint via http client(Postman) give me 200 and give me a Page.
I am testing some strategies but doesn't works.
Thanks

Related

#ControllerAdvice not triggered on unit testing

I need to test #ControllerAdvice methods which are in service layer. But I faced two issues:
ExceptionHandlers not triggered
And even if it would be triggered, I find out that don't now how to test it)
#Slf4j
#ControllerAdvice
public class AppExceptionHandler {
#ExceptionHandler(value = {.class})
public ResponseEntity<Object> handleMyException(MyException ex, WebRequest request) {
ErrorMessage errorMessage = ErrorMessage.builder()
.message(ex.getMessage())
.httpStatus(HttpStatus.BAD_REQUEST)
.time(ZonedDateTime.now())
.build();
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
#RunWith(MockitoJUnitRunner.class)
#RequiredArgsConstructor
public class MyExceptionTest {
private final AppExceptionHandler handler;
#Mock private final MyService service;
#Test
public void test() throws MyException {
when(service.create(any()))
.thenThrow(MyException .class);
}
}
For this purpose, you can write a test for your controller layer with #WebMvcTest as this will create a Spring Test Context for you that contains all #ControllerAdvice.
As your service is throwing this exception, you can mock the service bean with #MockBean and use Mockito to instruct your bean to throw the expected exception.
As I don't know how your controller looks like, the following is a basic example:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
class PublicControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MyService myService;
#Test
public void testException() throws Exception {
when(myService.create(any())).thenThrow(new MyException.class);
this.mockMvc
.perform(get("/public"))
.andExpect(status().isBadRequest());
}
}

MockMvc perform(post()) test fails with NullPointerException

I have the following controller
#RequestMapping("/locations")
#AllArgsConstructor
#RestController
public class LocationController {
private final LocationService locationService;
#PostMapping
public ResponseEntity<LocationDTO> createLocation(#Valid #RequestBody LocationDTO locationDTO) {
Location location = locationService.createLocation(toLocation(locationDTO));
URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(location.getId())
.toUri();
return ResponseEntity.created(uri).body(toDTO(location));
}
//other methods
}
and the tests
#WebMvcTest(LocationController.class)
class LocationControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private LocationService locationService;
#MockBean
private MappingMongoConverter mappingMongoConverter;
#WithMockUser(value = "test")
#Test
void createLocation() throws Exception {
GeoJsonPoint testGeoJsonPoint = new GeoJsonPoint(123, 123);
LocationProperties testLocationProperties = new LocationProperties("testName", "testDesc");
Location testLocation = new Location("testId", testGeoJsonPoint, testLocationProperties);
String locationDTOString = objectMapper.writeValueAsString(toDTO(testLocation));
mvc.perform(post("/locations")
.contentType(APPLICATION_JSON)
.content(locationDTOString)
.characterEncoding("utf-8"))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(content().json(locationDTOString))
.andExpect(header().string("uri", "http://localhost:8080/api/locations/testId"));
}
}
Test results:
Resolved Exception: Type = java.lang.NullPointerException
java.lang.AssertionError: Status expected:<201> but was:<500>
Expected :201
Actual :500
Seems like Location location = locationService.createLocation(toLocation(locationDTO)); this location is set to null. How do I fix this? Any help is appreciated.
The mocked LocationService is probably returning null (it does in my test). For this test, the LocationService should be a real instance of the Location Service and not a mock.
I had to mock the behavior of the service since its a mockBean
when(locationService.createLocation(any())).thenReturn(testLocation);

Is it possible to use MockMvc and mock only the RestTemplate used by my service?

I'm trying to build one integration test using MockMvc and I want to mock only the RestTemplate used by MyService.java. If I uncomment the code on MyIT.java, this test will fail because the RestTemplate used by MockMvc will be mocked as well.
MyRest.java
#RestController
public class MyRest {
#Autowired
private MyService myService;
#RequestMapping(value = "go", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<String> go() throws IOException {
myService.go();
return new ResponseEntity<>("", HttpStatus.OK);
}
}
MyService.java
#Service
public class MyService {
#Autowired
private RestTemplate restTemplate;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public void go() {
restTemplate.getForObject("http://md5.jsontest.com/?text=a", String.class);
}
}
MyIT.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
//#RestClientTest(MyService.class)
public class MyIT {
#Autowired
private MockMvc mockMvc;
// #Autowired
// private MockRestServiceServer mockRestServiceServer;
#Test
public void shouldGo() throws Exception {
// mockRestServiceServer.expect(requestTo("http://md5.jsontest.com/?text=a"))
// .andRespond(withSuccess());
mockMvc.perform(get("/go")).andExpect(status().isOk());
}
}
First, you should #Autowired your RestTemplate bean to your test
class.
Then create the MockRestServiceServer with the restTemplate, instead of
autowiring it.
Perhaps try this one:
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockRestServiceServer;
#Before
public void setup() {
mockRestServiceServer= MockRestServiceServer.createServer(restTemplate);
}

How can I test my SpringBoot RestController using a MockMvc when I rely on a Spring Validator?

In my rest-controller I am validating the input JSON with a custom Spring validator class.
When I now want to write unit test for the controller then I am getting the error that my Validator could not be found...
I am using constructor injecting for the two components in my rest-controller.
#Autowired
public JobController(JobValidator jobValidator, JobService jobService) {
this.jobValidator = jobValidator;
this.jobService = jobService;
}
And here my corresponding Test class.
#RunWith(SpringRunner.class)
#WebMvcTest(JobsController.class)
#AutoConfigureMockMvc
public class MailMonitorJobControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private JobService jobService;
#Test
public void givenJobs_whenGetJobs_thenReturnJsonArray() throws Exception {
Job job = new Job("TEST");
List<Job> allJobs = Arrays.asList(job);
Mockito.when(jobService.getAllJobs()).thenReturn(allJobs);
mockMvc.perform(get("/api/v1/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Appreciate any help, hints or suggestions!
So thanks to #pvpkiran! I had to add the JobValidator also as a Mock!
#RunWith(SpringRunner.class)
#WebMvcTest(JobsController.class)
#AutoConfigureMockMvc
public class MailMonitorJobControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private JobService jobService;
#MockBean
private JobValidator jobValidator;
#Test
public void givenJobs_whenGetJobs_thenReturnJsonArray() throws Exception {
Job job = new Job("TEST");
List<Job> allJobs = Arrays.asList(job);
Mockito.when(jobService.getAllJobs()).thenReturn(allJobs);
mockMvc.perform(get("/api/v1/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type

I'm trying to mock rest api call but facing an error while testing the controller class using WebMvcTest,
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.abc.center.entities.repositories.SomeRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 41 more
I have an project structure something like this,
I tried many ways but no luck, Below is my restcontroller and its Mockito test class and repository,
#Slf4j
#Component
#RequestMapping()
#Setter
public class SomeController {
// Variable initialization
#Autowired
private SometRepository someRepository;
public void sendData(RecordNo rocordNo, String xmlString, SomeFile file) throws ValidationException{
ClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
header.add("x-api-key",api_key);
header.add("Content-Type",content_type);
header.add("Cache-Control",cache_control);
HttpEntity<String> request = new HttpEntity<>(xmlString, header);
try {
restTemplate.postForEntity(getUri(rocordNo,file), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException(e.getResponseBodyAsString());
}
}
getUri(RecordNo rocordNo SomeFile file){
// here I need someRepository which is an interface
}
}
public interface TestRepository extends PagingAndSortingRepository<RecordNo, Integer> {
//queries to repositories
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(SomeController.class)
public class SomeControllerTestClass {
private TestController serviceToTest;
private String xmlString = "String";
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Mock
private TestRepository testRepository;
#Before
public void init(){
serviceToTest.setTestRepository(testRepository);
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
MockitoAnnotations.initMocks(this);
}
#Test
public void verifySafe2Call() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.post("someuri")
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_JSON)
.content(xmlString)).andExpect(jsonPath("$.responseMessage").value("Validation succeeded"))
.andExpect(jsonPath("$.responseCode").value("SUCCESS"))
.andDo(print());
}
Does my project structure is making any problem? I know it is not able to find the definition but not getting why it is so.
Any suggestion would be nice.
Try this,
add following code to the controller:
#Autowired
private SometRepository someRepository;
public void setRepository(SomeRepository repository) {
this.repository = repository;}
I don't see #EnableJpaRepositories annotation.
Your config should be annotated with this annotation and Spring will scan the needed packages for repositories.
#Configuration
#EnableJpaRepositories(basePackages = {"com.abc.center.entities.repositories"})
public class JpaConfig {
}
I had a similar problem writing a unit/integration test with #WebMvcTest and I solved it by adding #ComponentScan("com.abc.center.entities.repositories") to SomeController (in your case).

Resources