MockMvc does not return created resource in post response - spring

Normally when you post to a spring data rest endpoint the response contains the location header with the url to the newly created resource and the json representation of the new resource in its body.
But when I post to MockMvc like following:
#RunWith(SpringRunner.class)
#SpringBootTest
public class OrderRestTest {
#Autowired
private ObjectMapper objectMapper;
#Autowired
private WebApplicationContext context;
#Autowired
private OAuthHelper oAuthHelper;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
public void testSuperuserCanCreateOrder() throws Exception {
RequestPostProcessor accessToken = oAuthHelper.addBearerToken("someSuperUser", "ROLE_SUPERUSER");
Order order = new Order();
order.salesMemo = "some sales memo";
String responseFromTestRestTemplate = objectMapper.writeValueAsString(order);
assertNotNull(responseFromTestRestTemplate);
mockMvc.perform(
post("/ErPApI/orders")
.with(accessToken)
.content(objectMapper.writeValueAsString(order))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful());
mockMvc.perform(
get("/ErPApI/orders")
.with(accessToken))
.andExpect(jsonPath("_embedded.orders", hasSize(1)))
.andExpect(jsonPath("_embedded.orders[0].salesMemo", is("some sales memo")))
.andReturn();
}
}
the post is successful but the response body is blank. Is there a way to simulate the real response with MockMvc? Is my setup wrong?

set the Accept header to application/json too in the request.

Related

Spring boot test, #WithMockUser produce NPE in Controller method

i have an issue with my controller test
So my base test class configured like so
#SpringBootTest
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
public abstract class BaseControllerTest extends DatabaseIT {
protected static long counter;
protected MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
protected ObjectMapper objectMapper;
protected MockRestServiceServer restServiceServer;
#Autowired
RestTemplate restTemplate;
#BeforeEach
protected void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
restServiceServer = MockRestServiceServer.createServer(restTemplate);
}
and test code
mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json(s));
When i debugging my controller method which is
public ResponseEntity<?> getSomething (
#Parameter(description = "ID") final String id,
#ApiIgnore #AuthenticationPrincipal Authentication user){
////
}
user value is null, though when i invoke SecurityContextHolder.getContext().getAuthentication() here in controller i'm getting Authentication object that's refers to one i mocked in
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
So recently i've updated my project to Java 17 , that also forced me to update spring boot to
version: '2.5.5'
and spring cloud to
"org.springframework.cloud:spring-cloud-dependencies:2020.0.5"

how to test rest controller with custom spring DispatcherServlet

I'm trying to test my controller and I want to my custom DispatcherServlet include on that.
my dispatcher is something like this:
public class LoggableDispatcherServlet extends DispatcherServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggableDispatcherServlet.class);
#Autowired
private LogsService logsService;
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws
Exception
{
.
.
.
mapper.readTree(requestWrapper.getContentAsByteArray());
int status = responseWrapper.getStatus();
JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray());
JsonNode responseJson= newNode;
logsService.addLogs(request,response ,...)
}
}
and my test Class is something like this:
#SpringBootTest
#RunWith(SpringRunner.class)
public class RestControllerTest {
private final Resource test1 = new ClassPathResource("test1.json");
private MockMvc mockMvc;
#Autowired
private RestTemplate restTemplate;
#Autowired
private WebApplicationContext wac;
#BeforeEach
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
this.mockRestServiceServer = MockRestServiceServer.bindTo(this.restTemplate)
.build()
}
#Test
public void getAvailableLoanTest() throws Exception{
String url1="http://localhost:80801/testService?param1=1&param2=2";
this.mockRestServiceServer
.expect(ExpectedCount.manyTimes(), requestTo(url1))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(this.test1, MediaType.APPLICATION_JSON_UTF8));
this.mockMvc.perform(post("/api/myApi")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(new MyRequest(1, 2,))))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType("application/json"))
//.andExpect(jsonPath("#.res1").value(1L))
.andReturn();
.
.
.
}
}
The problem is my request receive directly by rest controller and I can't test the DispatcherServlet.
I'm using spring boot 2.2.3 with jar packaging and I don't have web.xml or any servlet configration.

Spring test invalid configuration

I have a simple REST controller that I'm trying to test, the test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class ModelControllerTests {
private MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
private HttpMessageConverter jacksonConverter;
private MockMvc mockMvc;
#Mock
private ModelService service;
#InjectMocks
private ModelController controller;
#Autowired
private WebApplicationContext context;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
jacksonConverter = Arrays.stream(converters)
.filter(hmc -> hmc instanceof MappingJackson2HttpMessageConverter)
.findAny()
.orElse(null);
assertNotNull(jacksonConverter);
}
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controller)
.build();
}
#Test
public void postModel_returnsModel() throws Exception {
when(service.doSomething(any())).thenReturn(new Model("cde", null));
mockMvc.perform(post("/model")
.content(json(new Model("abc", null)))
.contentType(contentType))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{\"notEmpty\":\"abc\"}", true));
}
private String json(Object o) throws IOException {
var responseMessage = new MockHttpOutputMessage();
jacksonConverter.write(o, MediaType.APPLICATION_JSON_UTF8, responseMessage);
return responseMessage.getBodyAsString();
}
}
Now I've got a problem with dependencies and configuration, I've got the following line in my application.properties: spring.jackson.default-property-inclusion=non_null, which works fine when using the normal mockMvc (webAppContextSetup), however I wanted to mock ModelService (which is autowired in ModelController.
When using standaloneSetup to create MockMvc instance there seems to be no configuration, fields that are set to null are returned and furthermore it seems that the ModelService annotated with #Mock is not the same as the one in ModelController, therefore when postModel_returnsModel it's using the wrong service.
How can I solve this?

How to Mock client side rest service

I'm trying to create mockito test run for rest api below is the controller class followed by mock test which I'm trying to execute but the problem is it is still calling actual rest api instead of mocking it,
1) Controller Class
public void sendData(ID id, String xmlString, Records record) 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(id,record), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException("Error occurred while sending a file to some server "+e.getResponseBodyAsString());
}
}
2) Test class
#RunWith(MockitoJUnitRunner.class)
public class Safe2RestControllerTest {
private MockRestServiceServer server;
private RestTemplate restTemplate;
private restControllerClass serviceToTest;
#Before
public void init(){
//some code for initialization of the parameters used in controller class
this.server = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder(true).build();
}
#Test
public void testSendDataToSafe2() throws ValidationException, URISyntaxException {
//some code here when().then()
String responseBody = "{\n" +
" \"responseMessage\": \"Validation succeeded, message
accepted.\",\n" +
" \"responseCode\": \"SUCCESS\",\n" +
" 2\"responseID\": \"627ccf4dcc1a413588e5e2bae7f47e9c::0d86869e-663a-41f0-9f4c-4c7e0b278905\"\n" +
"}";
this.server.expect(MockRestRequestMatchers.requestTo(uri))
.andRespond(MockRestResponseCreators.withSuccess(responseBody,
MediaType.APPLICATION_JSON));
serviceToTest.sendData(id, xmlString, record);
this.server.verify();
}
}
How should I go ahead, any suggestion would be appreciated.
Spring's MVC test apparatus makes this quite easy.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = YourController.class)
public class YourControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testSendDataToSafe2() throws Exception {
// prepare e.g. create the requestBody
MvcResult mvcResult = mockMvc.perform(post(uri).contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().isOk())
.andReturn();
Assert.assertEquals(responseBody, mvcResult.getResponse().getContentAsString());
}
}
For more details, see the section titled "Add Unit Tests" here and/or the section titled "Auto-configured Spring MVC tests" here.
Your question also states "the problem is it is still calling actual rest api" so I'm guessing that, in addition to calling your controller is a test context, you want that mock out some of the behaviour of that controller. Specifically, you want to mock the RestTemplate instance used in that controller. If so, then would have to change the controller implementation such that the RestTemplate instance is an #Autowired class member. Then you would declare a mock for that in your test case like so:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = YourController.class)
public class YourControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private RestTemplate restTemplate;
#Test
public void testSendDataToSafe2() throws Exception {
// prepare e.g. create the requestBody
// tell your mocked RestTemplate what to do when it is invoked within the controller
Mockito.when(restTemplate.postForEntity(..., ..., ...)).thenReturn(...);
MvcResult mvcResult = mockMvc.perform(post(uri).contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().isOk())
.andReturn();
Assert.assertEquals(responseBody, mvcResult.getResponse().getContentAsString());
}
}
The above code is valid for spring-test:4.3.10.RELEASE.

java.lang.AssertionError: Status expected:<200> but was:<400> in spring jersey controller

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

Resources