I have the following controller:
#RestController
#RequestMapping(value = ROOT_MAPPING)
public class GatewayController {
#Autowired
private RequestValidator requestValidator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(requestValidator);
}
#PostMapping(value = REDIRECT_MAPPING)
public ResponseEntity<ResponseDTO> redirectEndpoint(#Validated #RequestBody RequestDTO requestDTO, BindingResult result) {
if (result.hasErrors()) {
// Handle validation errors
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// Do other stuff
return ResponseEntity.status(HttpStatus.OK).build();
}
}
And this test class:
#RunWith(SpringRunner.class)
#WebMvcTest(GatewayController.class)
public class GatewayControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private RequestValidator requestValidator;
#MockBean
private BindingResult bindingResult;
private JacksonTester<RequestDTO> requestJacksonTester;
#Before
public void setUp() throws Exception {
JacksonTester.initFields(this, new ObjectMapper());
Mockito.when(requestValidator.supports(ArgumentMatchers.any())).thenReturn(true);
}
#Test
public void whenRedirectWithValidationErrorsThenBadRequestReturned() throws Exception {
RequestDTO request = new RequestDTO();
// Set some values
Mockito.when(bindingResult.hasErrors()).thenReturn(true);
mockMvc.perform(MockMvcRequestBuilders.post(ROOT_MAPPING + REDIRECT_MAPPING)
.contentType(MediaType.APPLICATION_JSON)
.content(requestJacksonTester.write(request).getJson()))
.andExpect(MockMvcResultMatchers.status().isBadRequest());
}
}
When I run this code the test case fail with this reason: Status
Expected :400
Actual :200
So, what I want to do is to mock the BindingResult which passed as a parameter to the redirectEndpoint method in the Controller so that when calling bindingResult.hasErrors() this should return true and the test case pass.
I did many search but with no luck. Any suggestions how to do that?
Thanks in advance.
BindingResult is not a bean in the ApplicationContext. Thus, you cannot mock it via #MockBean.
A BindingResult is created for you by Spring MVC for each incoming HTTP request.
Thus, you don't want to mock the BindingResult. In fact, you probably don't want to mock the behavior of your RequestValidator either. Rather, you should ideally use the real implementation of your RequestValidator, pass in invalid request data (via MockMvc), and then verify the response accordingly.
Note that you should be able to include the real implementation of your RequestValidator via #Import(RequestValidator.class) on the test class.
Related
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());
}
}
I'm using SpringBoot 2 and Spring 5 (RC1) to expose reactive REST services. but I can't manage to write unit test for those controllers.
Here is my controller
#Api
#RestController
#RequestMapping("/")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(path = "/", method = RequestMethod.GET)
public Flux<MyModel> getPages(#RequestParam(value = "id", required = false) String id,
#RequestParam(value = "name", required = false) String name) throws Exception {
return myService.getMyModels(id, name);
}
}
myService is calling a database so I would like not to call the real one. (I don't wan't integration testing)
Edit :
I found a way that could match my need but I can't make it work :
#Before
public void setup() {
client = WebTestClient.bindToController(MyController.class).build();
}
#Test
public void getPages() throws Exception {
client.get().uri("/").exchange().expectStatus().isOk();
}
But I'm getting 404, seems it can't find my controller
You have to pass actual controller instance to bindToController method.
As you want to test mock environment, you'll need to mock your dependencies, for example using Mockito.
public class MyControllerReactiveTest {
private WebTestClient client;
#Before
public void setup() {
client = WebTestClient
.bindToController(new MyController(new MyService()))
.build();
}
#Test
public void getPages() throws Exception {
client.get()
.uri("/")
.exchange()
.expectStatus().isOk();
}
}
More test examples you can find here.
Also, I suggest switching to constructor-based DI.
it's my controller...
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Path("/categories")
public POSResponse getAllCategories() {
String countryCode="1";
return infoService.getAllCategories(countryCode);
}
it's my testController....
#Mock
InfoService infoService;
#InjectMocks
private InfoController infoController;
private MockMvc mockMvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(infoController).build();
}
#Test
public void getAllCategoriesTest() throws Exception {
POSResponse response=new POSResponse();
Category category=new Category();
category.setCountryCode(1);
category.setDescription("Mother Dairy");
response.setResponse(category);
when(infoService.getAllCategories("1")).thenReturn(response);
mockMvc.perform(get("/categories"))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.description", is("Mother Dairy")));
verify(infoService, times(1)).getAllCategories("1");
verifyNoMoreInteractions(infoService);
}
i am using jersey controller.
when i call the method i got error msg"java.lang.AssertionError: Status expected:<200> but was:<400>"
Whether you may use in your controller :
#Consumes(MediaType.APPLICATION_JSON) // instead of MediaType.APPLICATION_FORM_URLENCODED
Or, in you test :
mockMvc.perform(get("/categories")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
...
Why?
An HTTP request should be in one of the media types accepted by the server, and MockMvc might use MediaType.APPLICATION_JSON (As my test show!). You can check it by printing the request detail:
mockMvc.perform(get("/categories")
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andDo(MockMvcResultHandlers.print())
...
I'm using Spring MVC Framework and I want to test my Controllers with JUnit. How should I mock POST or GET parameters of a Controller and how will I access the Model's attributes to check its content? My Controller's signature is the following:
#RequestMapping(value="/findings", method=RequestMethod.POST)
public String findUsers(#RequestParam("userInput") String userInput, Model m)
You can use spring-test and mockito alongside junit to accomplish the task.
spring-test enables you to test controllers and a whole bunch of other things in spring
mockito is a great library for creating mocked classes
This is a very high level overview of unit testing a controller. This may not be correct for your situation, but should give you a bit of a starting point.
public class SomeControllerTest {
private SomeController controller;
#Mock
private View view;
private MockMvc mockMvc;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(controller)
.setSingleView(view)
.build();
}
#Test
public void test() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Model m = new Model();
MvcResult mvcResult = mockMvc.perform(post("/findings")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(m)))
.andExpect(status().isOK())
.andReturn();
}
}
For doing that I would recomend you a simple test where you instanciate your Controller (you can mock all the dependencies) and after that you call your method pasing Model.
public class MyController (){
MyDependencyOne one;
MyDependencyTwo two ;
#Autowired
public MyController (MyDependencyOne one, MyDependencyTwo two){
this.one = one;
this.two = two;
}
public String findUsers(#RequestParam("userInput") String userInput, Model
m){
// do whatever
}
}
public class MyControllerTest (){
#Test
public void myTest(){
//MOCK your dependencies
MyController controller = new MyController(one, two);
Model model = new ExtendedModelMap()
controller.filter(model);
assertEquals("yourAtribute", model.asMap().get("yourAtribute");
}
}
This are the annotated methods in the controller:
#RequestMapping(method = RequestMethod.GET)
public String getClient(#PathVariable("contractUuid") UUID contractUuid, Model model) {
ClientDto clientDto = new ClientDto();
clientDto.setContractUuid(contractUuid);
model.addAttribute("client", clientDto);
return "addClient";
}
#ModelAttribute("contract")
public ContractDto getContract(#PathVariable("contractUuid") UUID contractUuid) throws ContractNotFoundException {
return contractService.fromEntity(contractService.findByUuid(contractUuid));
}
The test method that I am trying is shown below, but it fails for attribute contract. The attribute client is added to the Model in a #RequestMapping method.
private MockMvc mockMvc;
#Autowired
private ContractService contractServiceMock;
#Autowired
private ClientService clientServiceMock;
#Autowired
protected WebApplicationContext wac;
#Before
public void setup() {
Mockito.reset(contractServiceMock);
Mockito.reset(clientServiceMock);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void test() throws Exception {
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
mockMvc.perform(get("/addClient/{contractUuid}", uuid))
.andExpect(status().isOk())
.andExpect(view().name("addClient"))
.andExpect(forwardedUrl("/WEB-INF/pages/addClient.jsp"))
.andExpect(model().attributeExists("client"))
.andExpect(model().attributeExists("contract"));
}
The contract attribute shows in the jsp page when I run the application, since I use some of its attributes, but since it fails in the test method is there another way to test it ?
It fails with the message:
java.lang.AssertionError: Model attribute 'contract' does not exist
Spring is 4.0.1.RELEASE
It seems it was my fault.
Even though the #ModelAttribute method returns an instance of ContractDto I only mocked one method used from the service:
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
and so findByUuid returned something, but contractService.fromEntity was left untouched so I had to also mock it:
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
ContractDto contractDto = new ContractDto(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
when(contractServiceMock.fromEntity(contract)).thenReturn(contractDto);