While trying to get my PUT MockMvc test to work, I found that JSON was not supported. Using an answer from this thread:
Configure MappingJacksonHttpMessageConverter
I was able to resolve this by extending the WebMvcConfigurationSupport class. However, when I use this override, it appears as though the ModelAndView data returned in my GET tests is now NULL. I know I can get the response data using this:
String content = result.getResponse().getContentAsString();
But, does anyone have an explanation for why the ModelAndView data is NULL?
Here is my MockMVC test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("test-rest-context.xml")
public class AccountControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
#Test
public void findCustomerByID() throws Exception {
MvcResult result = mockMvc.perform(get("/api/account/customers/{customerID}", "123").accept(contentType)
.param("fields", "id", "email", "userName")
.contentType(contentType))
.andExpect(status().isOk())
.andReturn();
ModelAndView mav = result.getModelAndView();
// mav is NULL here after I extend WebMvcConfigurationSupport class.
}
}
Related
I am writing a simple test for a controller endpoint.
It works fine when I do the following.
#SpringBootTest
#ContextConfiguration(classes = {
HomeController.class,
HomeControllerTest.class
})
class HomeControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private static final String URL = "/a";
private static final ObjectMapper objectMapper = new ObjectMapper();
#Test
public void test() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Request request = new Request();
mockMvc.perform(post(URL)
.contentType("application/json")
.content(objectMapper.writeValueAsString(request))
.andExpect(status().isOk());
}
}
But I do not want to have to create the mockMvc and concern with webApplicationContext.
Thus instead, attempting to use #AutoConfigureMockMvc instead as follows.
But this doesn't work. Fails with following error.
java.lang.AssertionError: Status expected:<200> but was:<403> Expected
:200 Actual :403
What am I doing wrong?
My attempt which throws above error.
#SpringBootTest
#AutoConfigureMockMvc // using this annotation instead
#ContextConfiguration(classes = {
HomeController.class,
HomeControllerTest.class
})
class HomeControllerTest {
// wiring mockMvc instead
// no webApplicationContext autowired
#Autowired
private MockMvc mockMvc;
private static final String URL = "/a";
private static final ObjectMapper objectMapper = new ObjectMapper();
#Test
public void test() throws Exception {
Request request = new Request();
mockMvc.perform(post(URL)
.contentType("application/json")
.content(objectMapper.writeValueAsString(request))
.andExpect(status().isOk());
}
}
Try create test like this exemple it is better.
#SpringBootTest
#AutoConfigureMockMvc
public class HomeControllerTest
private ObjectMapper mapper;
private MyControler myController;
private ServiceInSideConttroler service;
add your atributes
#Before
public init(){
this.mapper = new ObjectMapperConfiguration().mapper();
this.service = mock(ServiceInSideConttroler.class);
this.myController = new MyController(service);
}
#Test
public void test() throws Exception {
// exemple how mock reponse from any service or repository.
when(service.findById(any(Long.class)))
.thenReturn(Optional.of(budget));
Object resp = myController.save(mockben());
......
aserts(resp)
}
}
Remember, to work with the tests like this, do not use #Authwired in the attributes, only in the constructor of the classes annotated with #service, #componet, #controller etc .... that is, any class controlled by Spring that will use dependecia injection. Also rember asserts your reponse.
I've been trying to figure out why my mocked findIngredientsByCategory method is returning null when I have when(controller.findIngredientsByCategory(any()).thenReturn(Collections.emptyList()). This implementation works for the findAll method works.
Below is my implementation for my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientController ingredientController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Autowired
private ObjectMapper mapper;
private static class Behavior {
IngredientController ingredientController;
public static Behavior set(IngredientController ingredientController) {
Behavior behavior = new Behavior();
behavior.ingredientController = ingredientController;
return behavior;
}
public Behavior hasNoIngredients() {
when(ingredientController.getAllIngredients()).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientsByCategory(any())).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientById(anyString())).thenReturn(Optional.empty());
return this;
}
}
#Test
public void getIngredientsByCategoryNoIngredients() throws Exception {
Behavior.set(ingredientController).hasNoIngredients();
MvcResult result = mvc.perform(get("/ingredients/filter=meat"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andReturn();
String content = result.getResponse().getContentAsString();
System.out.println(content);
}
And below is the implementation for the controller:
#RestController
#RequestMapping("/ingredients")
public class IngredientController {
#Autowired
private IngredientRepository repository;
#RequestMapping(value = "/filter", method = RequestMethod.GET)
public List getIngredientsByCategory(#RequestParam("category") String category) {
return repository.findByCategory(category);
}
}
I'm not sure why the mock controller is returning null with this request, when I tell it to return an empty list. If someone could please help with this I would greatly appreciate it! Thanks.
Th request path in test is "/ingredients/filter=meat", but it should be "/ingredients/filter?category=meat". So, it seem that getIngredientsByCategory was not called.
The MockMvc actually will call the IngredientController that is bootstrapped and created by the Spring Test framework but not call the mocked IngredientController that you annotated with #MockBean, so all the stubbing that you made will not be called.
Actually, the point of #WebMvcTest is to test #RestController and its related Spring configuration is configured properly , so a real instance of IngredientController is necessary to create rather than using a mocked one. Instead , you should mock the dependencies inside IngredientController (i.e IngredientRepository).
So , the codes should looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientRepository ingredientRepository;
#Test
public void fooTest(){
when(ingredientRepository.findByCategory(any()).thenReturn(Collections.emptyList())
//And use the MockMvc to send a request to the controller,
//and then assert the returned MvcResult
}
}
I am trying to test a RestTemplate exchange with a response entity in a controller from a rest Service in another application. My Junit test is coming back with a null ResponseEntity. I have tried a couple ways with no luck. First tried using mockito to mock response methods (when...then). Second tried with Exchange matchers. I also tried to mix the two with no luck. Any help would be great. Here is my Controller response I am creating:
ResponseEntity<RestResult<List<SiteMessage>>> response = rest.exchange(getAllMessagesUrl, HttpMethod.GET,
HttpEntity.EMPTY, new ParameterizedTypeReference<RestResult<List<SiteMessage>>>() {
});
Here is my Junit Test:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class MessageControllerTests {
#InjectMocks
MessageController messageController;
#Mock
RestTemplate restTemplate;
#Mock
SiteMessageService serviceMock;
#Mock
ResponseEntity<RestResult<List<SiteMessage>>> response;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(messageController).build();
}
#Test
public void testListPage() throws Exception {
RestResult<List<SiteMessage>> result = new RestResult<>();
result.setMessage("success");
SiteMessage message1 = new SiteMessage();
SiteMessage message2 = new SiteMessage();
message1.setId("ldsf030j2ldjs");
message2.setId("ldsf0432234s");
List<SiteMessage> messageList = new ArrayList<>();
messageList.add(message1);
messageList.add(message2);
result.setResults(messageList);
when(response.getBody()).thenReturn(result);
when(response.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);
when(restTemplate.exchange(
Matchers.any(URI.class),
Matchers.eq(HttpMethod.GET),
Matchers.any(),
Matchers.<ParameterizedTypeReference<RestResult<List<SiteMessage>>>>any())
).thenReturn(response);
mockMvc.perform(get("/message/list")).andExpect(status().isOk()).andExpect(view().name("message/list"));
}
}
I am trying to return a response with a body containing a RestResult object, which has a list of messages and a message
I use Spring MVC and Spring boot to write a Restful service. This code works fine through postman.While when I do the unit test for the controller to accept a post request, the mocked myService will always initialize itself instead of return a mocked value defined by when...thenReturn... I use verify(MyService,times(1)).executeRule(any(MyRule.class)); and it shows the mock is not used.
I also tried to use standaloneSetup for mockMoc, but it complains it can't find the mapping for the path "/api/rule".
Could anybody help to figure out the problem?
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class MyControllerTest {
#Mock
private MyService myService;
#InjectMocks
private MyController myRulesController;
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void controllerTest() throws Exception{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
Long userId=(long)12345;
MyRule happyRule = MyRule.createHappyRule(......);
List<myEvent> mockEvents=new ArrayList<myEvent>();
myEvents.add(new MyEvent(......));
when(myService.executeRule(any(MyRule.class))).thenReturn(mockEvents);
String requestBody = ow.writeValueAsString(happyRule);
MvcResult result = mockMvc.perform(post("/api/rule").contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isOk())
.andExpect(
content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
verify(MyService,times(1)).executeRule(any(MyRule.class));
String jsonString = result.getResponse().getContentAsString();
}
}
Below is my controller class, where MyService is a interface. And I have implemented this interface.
#RestController
#RequestMapping("/api/rule")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST,consumes = "application/json",produces = "application/json")
public List<MyEvent> eventsForRule(#RequestBody MyRule myRule) {
return myService.executeRule(myRule);
}
}
Is api your context root of the application? If so remove the context root from the request URI and test. Passing the context root will throw a 404. If you intend to pass the context root then please refer the below test case. Hope this helps.
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#InjectMocks
private MyController myRulesController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = standaloneSetup(myRulesController).build();
}
#Test
public void controllerTest() throws Exception{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
MyController.User user = new MyController.User("test-user");
ow.writeValueAsString(user);
MvcResult result = mockMvc.perform(post("/api/rule").contentType(MediaType.APPLICATION_JSON).contextPath("/api")
.content(ow.writeValueAsString(user)))
.andExpect(status().isOk())
.andExpect(
content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
}
}
Below is the controller
/**
* Created by schinta6 on 4/26/16.
*/
#RestController
#RequestMapping("/api/rule")
public class MyController {
#RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public User eventsForRule(#RequestBody User payload) {
return new User("Test-user");
}
public static class User {
private String name;
public User(String name){
this.name = name;
}
}
}
Repository object not mocked from controller testcase return empty object here is the below code
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Main.class)
#WebAppConfiguration
#ActiveProfiles(ApplicationConstants.DEVELOPMENT_PROFILE)
public class EmployeeControllerRealTest {
#Autowired
private WebApplicationContext webAppContext;
private MockMvc mockMvc;
#Mock
EmployeeRepository employeeRepository;
#InjectMocks
EmployeeCompositeService employeeCompositeService;
#InjectMocks
EmployeeService employeeService;
#InjectMocks
EmployeeController employeeController;
String name = "mike";
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
MockitoAnnotations.initMocks(this);
}
#Test
public void testGetEmployees() throws Exception {
Mockito.when(employeeRepository.findByName(name)).thenReturn(getEmployees());
String url = URIConstants.ROOT_CONTEXT + URIConstants.EMPLOYEE;
MvcResult result =
mockMvc.perform(post(url)
.contentType(APPLICATION_JSON_UTF8)
.content(convertObjectToJsonBytes(name))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$[0].employeeName").value("Mike"))
.andReturn();
String jsonContent = result.getResponse().getContentAsString();
LOGGER.debug("jsonContent: {}",jsonContent);
}
protected byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
private List<Employee> getEmployees(){
//here is the logic to get List of employees to return. When the mockito call is invoked.
}
}
I have repository call to invoke findByName("mike") in employeeServiceImpl So I doesn't want to hit the database So I have mocked in my Controller method i.e testGetEmployees()
When I run this test case it invoke EmployeeController method and it's call to EmployeeCompositeService method than it's call to EmployeeService method in this service method
has a repository call that call mocked in this controller test method. When I debug it returns empty list. What I did wrong in my controller testcase.
Can you please help me Thanks in Advance.