How do I give request parameters for a POST using MockMvc - java

I try to junit the following controller
#RequestMapping(value="actions.htm", params="reqType=delete",method=RequestMethod.POST)
#ResponseBody
public String deletePendingAction(#RequestParam("aPk") Long aPk)
{
pendingActionsService.deletePendingAction(aPk);
return "Deleted";
}
I use params="reqType=delete" and this is I think the reason why junit fails to map to the controller. I tested all other controllers and they work fine without params tag to the controller. My junit config is:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = ApplicationConfig.class,loader = AnnotationConfigWebContextLoader.class)
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class JUnitTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void SetupContext()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void testController() throws Exception
{
this.mockMvc.perform(post("/actions.htm","reqType=delete").param("aPk","2"));
}
}
How do I translate this params tag to the spring mvc junit? Thank you

add param `reqType=delete' to url.
this.mockMvc.perform(post("/actions.htm?reqType=delete")
.param("aPk", "2")).andReturn().getResponse().getStatus());

In fact the test should be like this, based on your controller:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Assert;
final MvcResult result = mockMvc
.perform(post("/actions.htm")
.param("reqType", "delete")
.param("aPk", "2"))
.andExpect(
status().isOk()
.andReturn());
final String content = result.getResponse().getContentAsString();
Assert.assertEquals("Deleted", content);

Related

Junit MockMvc perform POST with path variable in URL return 404 not found

I have a SpringBoot application with this method in the controller to create an user in the database. The controller is working fine in Postman.
#RestController
#RequestMapping("/v1")
public class UserController {
#PostMapping(value = "/user/{id}")
public void createUser(#PathVariable Integer id, #Valid #RequestBody User request,
BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(VALIDATION_ERRORS, bindingResult.getFieldErrors());
}
userService.createUser(id, request), HttpStatus.CREATED);
}
Now I have a junit test case to test this method and I am getting a 404
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
public class UserTest {
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
But in the log, I was able to see the correct url:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v1/user/10
Parameters = {}
Can someone please let me know why I am getting a 404 NOT Found? Thanks.
From docs you need #AutoConfigureMockMvc on class and #Autowire MockMvc
Another useful approach is to not start the server at all, but test only the layer below that, where Spring handles the incoming HTTP request and hands it off to your controller. That way, almost the full stack is used, and your code will be called exactly the same way as if it was processing a real HTTP request, but without the cost of starting the server. To do that we will use Spring’s MockMvc, and we can ask for that to be injected for us by using the #AutoConfigureMockMvc annotation on the test case:
Code :
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserTest {
#Autowire
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
}
If want to Test your real springboot url Test (End to end Test)
u can use rest-assured or resttemplte
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
#TestPropertySource(value={"classpath:application.properties"})
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class SpringRestControllerTest {
#Value("${server.port}")
int port;
#Test
public void getDataTest() {
get("/api/tdd/responseData").then().assertThat().body("data", equalTo("responseData"));
}
#Before
public void setBaseUri () {
RestAssured.port = port;
RestAssured.baseURI = "http://localhost"; // replace as appropriate
}
}
https://dzone.com/articles/test-driven-development-with-spring-boot-rest-api

Spring Boot controller test - assert static HTML response

I am trying to write a unit test to verify that a request returns the content of a static html file. The page is rendered when running the server, but there is no content in the test response.
Controller class:
#Controller
public class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "index.html";
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnIndexPage() throws Exception {
File index = new ClassPathResource("static/index.html").getFile();
String html = new Scanner(index).useDelimiter("\\z").next();
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index.html"))
.andExpect(content().string(html));
}
}
What am I missing?
EDIT:
I've got a working test which involves actually starting the server. My goal however was to not have to do that, by using #WebMvcTest. Not sure if that's possible or not. I consider this a workaround (unless it's the only way) and am still looking for a solution (that doesn't require starting the server).
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class IndexControllerTest {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void shouldReturnIndexPage() throws Exception {
File index = new ClassPathResource("static/index.html").getFile();
String html = new Scanner(index).useDelimiter("\\z").next();
String responseBody = restTemplate.getForObject("/", String.class);
assertThat(responseBody).isEqualTo(html);
}
}
I came across this question facing the same issue, I managed to build a test using the MockMvc like this
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class homePageControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testIndex() throws Exception{
File login = new ClassPathResource("static/login.html").getFile();
String html = new String(Files.readAllBytes(login.toPath()));
this.mockMvc.perform(get("/login.html"))
.andExpect(status().isOk())
.andExpect(content().string(html))
.andDo(print());
}
}
When testing by doing http request, you have to spawn the whole server, so he can handle the request i.e headers, status and such
hope it helps

Spring boot RestEasy Post method Expected :201 Actual :404

I have a spring boot application which contains a RestEasy webservice created using #Service like:
#Path("/developers")
#Service
public interface DeveloperResource {
#POST
#Produces("application/json")
#Consumes("application/json")
Response create(#RequestBody List<DeveloperDto> developers);
}
and I have the the according integration test class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class DeveloperResourceTest {
public static final String URI = "http://localhost:8080/developers";
public static final DeveloperDto DEVELOPER = new DeveloperDto(null, "toto");
public static final List<DeveloperDto> DEVELOPERS_COLLECTION = Collections.singletonList(DEVELOPER);
public static final DeveloperEntity DEVELOPER_MAPPED_TO_ENTITY = DeveloperMapper.toEntity(DEVELOPER);
public static final String DEVELOPER_COLLECTION_IN_JSON = "[{\"developerId\":null,\"developerName\":\"toto\",\"programmingLanguages\":null}]";
private MockMvc mockMvc;
#MockBean
DeveloperService service;
#Mock
private DeveloperResource tested;
#Autowired
WebApplicationContext webApplicationContext;
private ObjectMapper mapperJson;
#Before
public void setUp() throws Exception {
tested=new DeveloperResourceImpl(service);
mockMvc=MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
mapperJson = new ObjectMapper();
}
#Test
public void should_create_a_developer_and_return_OK() throws Exception {
Mockito.when(service.save(DEVELOPER_MAPPED_TO_ENTITY)).thenReturn(Optional.of(DEVELOPER_MAPPED_TO_ENTITY));
tested.create(DEVELOPERS_COLLECTION);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post(URI)
.content(DEVELOPER_COLLECTION_IN_JSON)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
assertEquals(HttpStatus.CREATED.value(), response.getStatus());
assertEquals(URI, response.getHeader(HttpHeaders.LOCATION));
}
}
After the execution of the test I got:
java.lang.AssertionError:
Expected :201
Actual :404
My questions are:
Is my integration test configuration apropriate?
Can we use MockMvc even if the created REST web service isn't created using #RestController or #Controller?.
Thank you in advance
#Service will not be recognized as a rest service. You should annotate it as #RestController on a class, actually implementing the create method.
see: #Service
You can’t use MockMvc to test an app that doesn’t use Spring MVC. RESTdocs has support for Rest Assured which works over HTTP so it doesn’t depend on the web stack.

Inject mock into Spring MockMvc WebApplicationContext

I'm working to test (via JUnit4 and Spring MockMvc) a REST service adapter using Spring-boot. The adapter simply passes along requests made to it, to another REST service (using a custom RestTemplate) and appends additional data to the responses.
I'd like to run MockMvc tests to perform controller integration tests, but want to override the RestTemplate in the controller with a mock to allow me to predefine the 3rd party REST response and prevent it from being hit during each test. I've been able to accomplish this by instantiating a MockMvcBuilders.standAloneSetup() and passing it the controller to be tested with the mock injected as listed in this post (and my setup below), however I am not able to do the same using MockMvcBuilders.webAppContextSetup().
I've been through a few other posts, none of which answer the question as to how this might be accomplished. I would like to use the actual Spring application context for the tests instead of a standalone to prevent any gaps as the application is likely to grow.
EDIT: I am using Mockito as my mocking framework and am trying to inject one of its mocks into the context. If this isn't necessary, all the better.
Controller:
#RestController
#RequestMapping(Constants.REQUEST_MAPPING_PATH)
public class Controller{
#Autowired
private DataProvider dp;
#Autowired
private RestTemplate template;
#RequestMapping(value = Constants.REQUEST_MAPPING_RESOURCE, method = RequestMethod.GET)
public Response getResponse(
#RequestParam(required = true) String data,
#RequestParam(required = false, defaultValue = "80") String minScore
) throws Exception {
Response resp = new Response();
// Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_DATA, data);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
resp = template.getForObject(Constants.RESTDATAPROVIDER_URL, Response.class, parameters);
if(resp.getError() == null){
resp.filterScoreLessThan(new BigDecimal(minScore));
new DataHandler(dp).populateData(resp.getData());
}
return resp;
}
}
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
#TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String controllerURL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
private static String compressedParams_all = "?data={data}&minScore={minScore}";
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#InjectMocks
private Controller Controller;
#Mock
private RestTemplate rt;
#Value("${file}")
private String file;
#Spy
private DataProvider dp;
#Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testGetResponse() throws Exception {
String[] strings = {"requestData", "100"};
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(controllerURL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}
Spring's MockRestServiceServer is exactly what you're looking for.
Short description from javadoc of the class:
Main entry point for client-side REST testing. Used for tests that involve direct or indirect (through client code) use of the RestTemplate. Provides a way to set up fine-grained expectations on the requests that will be performed through the RestTemplate and a way to define the responses to send back removing the need for an actual running server.
Try to set up your test like this:
#WebAppConfiguration
#ContextConfiguration(classes = {YourSpringConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ExampleResourceTest {
private MockMvc mockMvc;
private MockRestServiceServer mockRestServiceServer;
#Autowired
private WebApplicationContext wac;
#Autowired
private RestOperations restOperations;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
mockRestServiceServer = MockRestServiceServer.createServer((RestTemplate) restOperations);
}
#Test
public void testMyApiCall() throws Exception {
// Following line verifies that our code behind /api/my/endpoint made a REST PUT
// with expected parameters to remote service successfully
expectRestCallSuccess();
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/my/endpoint"))
.andExpect(status().isOk());
}
private void expectRestCallSuccess() {
mockRestServiceServer.expect(
requestTo("http://remote.rest.service/api/resource"))
.andExpect(method(PUT))
.andRespond(withSuccess("{\"message\": \"hello\"}", APPLICATION_JSON));
}
}
Here's another solution. Simply put, it just creates a new RestTemplate bean and overrides the one already registered.
So while it performs produces the same functionality as #mzc answer, it allows me to use Mockito to craft the response and verification matchers a bit easier.
Not that it's more than a couple lines of code, but it also prevents from having to add additional code to convert from the Response object to a string for the above mockRestServiceServer.expect().andRespond(<String>) method's arg.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
#TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String Controller_URL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
#Configuration
static class Config {
#Bean
#Primary
public RestTemplate restTemplateMock() {
return Mockito.mock(RestTemplate.class);
}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#InjectMocks
private Controller Controller;
#Mock
private RestTemplate rt;
#Value("${file}")
private String file;
#Spy
private DataProvider dp;
#Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.rt = (RestTemplate) this.wac.getBean("restTemplateMock");
}
#Test
public void testGetResponse() throws Exception {
String[] strings = {"request", "100"};
//Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_SINGLELINE, strings[0]);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(Controller_URL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}
org.springframework.boot.test.mock.mockito.MockBean #MockBean helped me out.

With Spring, #InjectMock annotated test target doesn't use mocks

I'm trying to unit test a Spring 4.0.0 MVC application.
My controller is defined as follow:
#Controller
#RequestMapping("/test")
public class TestCtrl {
#Autowired
private TestService testService;
#Autowired
private TestRessourceAssembler testRessourceAssembler;
#Autowired
private ResponseComposer responseComposer;
#RequestMapping(value = "", method = RequestMethod.GET,produces = "application/json")
public HttpEntity showAll(Pageable pageable) {
Page<Test> patr = testService.getAll(pageable);
return responseComposer.composePage(patr,testRessourceAssembler);
}
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public HttpEntity<TestRessource> show(#PathVariable String name) {
Test test = testService.getOne(name);
if(test == null){
return new ResponseEntity("Erreur !",HttpStatus.NOT_FOUND);
}
return responseComposer.compose(test,testRessourceAssembler);
}
}
My controller unit test is as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#WebAppConfiguration
#ContextConfiguration(classes = {ApplicationConfig.class, TestMongoConfig.class, RestConfig.class, WebMvcConfig.class})
public class TestCtrlTests{
#InjectMocks
TestCtrl testCtrl;
#Mock
TestService testService;
#Autowired
protected WebApplicationContext wac;
protected MockMvc mockMvc;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
when(testService.getOne("jexiste")).thenReturn(new com.thalesgroup.ito.c2s.mc.portail.test.domain.Test("jexiste",1990));
when(testService.getOne("plaf")).thenReturn(null);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void simpleGetAnswer() throws Exception{
assertNotNull(mockMvc);
mockMvc.perform(get("/test")).andExpect(status().isOk());
mockMvc.perform(get("/test/jexiste")).andExpect(status().isOk());
mockMvc.perform(get("/test/plaf")).andExpect(status().isNotFound());
}
}
When I'm running the test, the "normal" TestService bean is injected and used (I can see the trace in the log), not the mock.
So I read some things on the internet and replaced
this.mockMvc = webAppContextSetup(this.wac).build();
with
this.mockMvc = standaloneSetup(TestCtrl.class).build();
But, and I knew it would happen, I've no more Spring context when doing this, so my PageableArgumentResolver and my other beans (testRessourceAssembler, responseComposer) aren't injected anymore... So they are Null and happen a NullPointerException.
My question is:
1) I'm I designing something wrong ?
2) If not, how can I inject a mock in my controller while keeping other beans from the context ?
Thanks to you !
I'm looked into your tests and this should work. Simply build your MockMvc on your controller with mocked beans. After this all mocks will be visible inside test.
A MockMvcBuilder that accepts #Controller registrations thus allowing full control over the instantiation and the initialization of controllers and their dependencies similar to plain unit tests, and also making it possible to test one controller at a time.
Don't use Spring Integration test! This is simple unit testing!
Fixed test
#RunWith(MockitoJUnitRunner.class)
public class TestCtrlTests{
#InjectMocks
TestCtrl testCtrl;
#Mock
TestService testService;
protected MockMvc mockMvc;
#Before
public void setup(){
when(testService.getOne("jexiste")).thenReturn(new com.thalesgroup.ito.c2s.mc.portail.test.domain.Test("jexiste",1990));
when(testService.getOne("plaf")).thenReturn(null);
this.mockMvc = standaloneSetup(testCtrl).build();
}
#Test
public void simpleGetAnswer() throws Exception{
assertNotNull(mockMvc);
mockMvc.perform(get("/test")).andExpect(status().isOk());
mockMvc.perform(get("/test/jexiste")).andExpect(status().isOk());
mockMvc.perform(get("/test/plaf")).andExpect(status().isNotFound());
}
}

Categories