How to write mockito test for post request - java

This function is used to update the user details in the database. can someone help me to write test cases for this function.
#RequestMapping(value = "/updateDetails", method = RequestMethod.POST)
public String updateVendorDetails(#Valid #ModelAttribute("users") Users users, BindingResult result,Model model) {
logger.info("{}.{}",new VendorController().getClass().getPackageName(), new VendorController().getClass().getName());
if(result.hasErrors()) {
model.addAttribute("edit","edit");
logger.warn("Function: updateVendorDetails(), Information: Error while updating vendor details");
return register.toString();
}
userDao.updateVendorDetails(users);
logger.info("Function: updateVendorDetails(), Information: Vendor details updated successfully");
return vendor.toString();
}
Update
Code:
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
Resulting error:
This says that post method is forbidden and my test fails
This is my Test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class TestVendorPage {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(roles = "VENDOR")
#Test
public void testIfUpdateEdtailsIsAvailableOnlyForVendor() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("firstName", "vinod");
params.add("lastName", "babu");
params.add("contactNumber", "9952016709");
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
}
}

Regarding your update:
Thank you for clarifying your post with a specific error/specific problem.
For that specific error - HTTP 403: Forbidden - this should resolve the problem:
Unit test Springboot MockMvc returns 403 Forbidden
i think probleam is happend in "mockMvc" object is not
autowired.mockMvc object should load from WebApplicationContext in
before program run.
Please - PLEASE - consider looking at one or more of the links I cited above.
baeldung.com: Testing in Spring Boot
spring.io: Testing the Web Layer
mkyong.com: Spring REST Integration Example
I've found all three sites very valuable resources. Time spent with these tutorials will help you a great deal!

Related

Spring web server considers part of uri as path variable during testing

There are two kind of similar endpoints, let's assume:
POST devices/{uuid}/{imei} and POST devices/{uuid}/device-info. The first one is to update IMEI (delivered via path variable) of device specified by UUID and the second one is to update its other parameters (delivered with request as json body).
While server is working "normally" from a jar file, both endpoints works properly how it is described above, which was tested by Postman. But when I run integration tests (with maven or directly through IntelliJ), sending POST request to devices/{uuid}/device-info is interpret on server side as a request to devices/{uuid}/{imei}, where phrase "device-info" is treated as IMEI number.
For integration tests I use autoconfigured MockMvc class and SpringBootTest + Mockito + JUnit4 utilities. webEnvironment is set as SpringBootTest.WebEnvironment.MOCK and everything is ran with SpringRunner.
I was looking for solutions, but actually found nothing. Has anyone met with something similar?
EDIT:
I'm adding API declarations if it can help.
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
ResponseEntity<Void> updateDeviceInfo(#Valid #RequestBody DeviceInfo deviceInfo);
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/{imei}")
ResponseEntity<Void> updateDeviceImei(#PathVariable("deviceUuid") UUID deviceUuid, #PathVariable("imei") String imei);
The test itself is as simple as it can be:
DeviceInfo deviceInfo = this.prepareDeviceInfo();
String url = String.format("/v3/devices/%s/device-info", super.firstDeviceUuid);
mvc.perform(put(url)
.content(asJsonString(deviceInfo)))
.andExpect(status().is(204));
where asJsonString is simple helper method to prepare JSON from an object with Jackson methods.
Not sure what is the problem in your case. But I tried this code and it works for me
#RestController
#Slf4j
public class DeviceController {
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device info successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/device-info", consumes = {"application/json"})
ResponseEntity<Void> updateDeviceInfo(#RequestBody Product product, #PathVariable("deviceUuid") UUID deviceUuid){
log.info("Inside updateDeviceInfo");
return ResponseEntity.ok().build();
};
#ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "Device IMEI successfully updated")
#PutMapping(value = "/devices/{deviceUuid}/{imei}")
ResponseEntity<Void> updateDeviceImei(#PathVariable("deviceUuid") UUID deviceUuid, #PathVariable("imei") String imei){
log.info("Inside updateDeviceInfo");
return ResponseEntity.ok().build();
};
}
For test cases
#SpringBootTest
#AutoConfigureMockMvc
public class DeviceControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#Test
public void test() throws Exception {
Product product = new Product();
String url = String.format("/devices/%s/device-info", UUID.randomUUID().toString());
mvc.perform(put(url)
.content(objectMapper.writeValueAsString(product)))
.andExpect(status().is(204));
}
#Test
public void test2() throws Exception {
Product product = new Product();
String url = String.format("/devices/%s/%s", UUID.randomUUID().toString(),UUID.randomUUID().toString());
mvc.perform(put(url))
.andExpect(status().is(204));
}
}
I've finally found an answer. When I just commented devices/{uuid}/{imei} endpoint handler in controller, test's result status was 415, so it looked like no handler was found in controller. Then I found this solution: Spring MVC testing results in 415 error which worked for me perfectly.
I just set in my test case a content type to MediaType.APPLICATION_JSON_UTF8 as below and thanks to that it was correctly interpret on the server side.
mvc.perform(put(url)
.content(mapper.writeValueAsString(deviceInfo))
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().is(204));
EDIT: MediaType.APPLICATION_JSON works well too.

com.sun.xml.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/plain. Is this an error message instead of a SOAP response?

Hello I am trying to create a dirty test for my soap integration test. I just got SSL working on my spring boot app and I wanted to see if it will hit my soap end point.
When I run man verify on my integration test I get this error:
com.sun.xml.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/plain. Is this an error message instead of a SOAP response?
Here is my test code :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {EndPointTestConfiguration.class
})
public class SoapIT {
private static ApplicationContext context;
#BeforeClass
static public void setup(){
SpringApplication springApplication = new SpringApplicationBuilder()
.sources(MockServerApp.class)
.build();
context = springApplication.run();
}
#Autowired
private String studyDetailDemo;
#Test
public void soapTest() throws ClientProtocolException, IOException {
String result = Request.Post("https://127.0.0.1:28443/nulogix/ws/billingtool")
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(studyDetailDemo, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
}
}
I am kind of new to integration testing and have no idea what this error means
Thank you for any help
I think you need to read up a bit on how to do spring testing.
testing-with-mock-environment
#SpringBootTest will automatically scan for spring annotated classes and load up a mockspringcontext for you so you dont need to do all the #BeforeClass things.
If you want to call this "Mock context" you need configure and autowire in a MockMvc, WebTestClient or TestRestTemplate.
On the other hand if you want to start a real server you need to specify #SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) (or a defined port).
You can read all about it in the linked documentation above.
And btw, you can't autowire in a string.
Your code should look something like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class SoapIT {
#LocalServerPort
private int port;
private String studyDetailDemo = "some body text";
#Test
public void soapTest() throws ClientProtocolException, IOException {
String result = Request.Post("https://127.0.0.1:" + port + "/nulogix/ws/billingtool")
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(studyDetailDemo, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
}
}
Havn't tried the code, wrote it on mobile.

Testing session invalidation in Spring Security

I am playing around with Spring Security and now I am trying get some knowledge about testing my REST-controller with regards to security.
So I prepared my test-class with:
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
containing test-cases mostly of the following or similar form:
#Test
public void handleSecuredRequest_shouldReturn200_withAdminUser() throws Exception{
ResultActions action = mvc.perform(get("/secured").with(user("admin").roles("ADMIN")));
int status = action.andReturn().getResponse().getStatus();
assertTrue("expected status code = 200 ; current status code = " + status, status == 200);
}
What I was not able to achieve until now were things concerning sessions.
Most notably I would be interested to verify that session-invalidation is performed correctly.
How can I achieve that?
EDIT:
I was able to find something close to a solution doing the following based on
https://stackoverflow.com/a/26281932/6294605 :
#Test
public void logout_shouldInvalidateSession_withLoggedInUser() throws Exception{
ResultActions action = mvc.perform(get("/userAsJSON").with(user("user")));
MockHttpSession session = (MockHttpSession) action.andReturn().getRequest().getSession();
ResultActions action2 = mvc.perform(post("/logout").session(session));
ResultActions action3 = mvc.perform(get("/userAsJSON").session(session));
int status3 = action3.andReturn().getResponse().getStatus();
assertTrue("expected status code = 401 ; current status code = " + status3, status3 == 401);
}
But I am not entirely satisfied with this.
It requires to process several steps to let me test what I want.
Resulting from 1.: it is not detached from certain other things to function correctly (e.g. the "/userAsJSON" endpoint returning 401 for not authenticated users).
Resulting from 2.: it requires additional attention to make sure that test-cases exist that ensure that the formal requirements for my test to be valid are met.
So I would wish for an option to make this less error-prone.
Any ideas?

Documenting PUT REST call without body and with path and query parameters

There's REST API call designed via HTTP PUT that has only path and query parameters and does not need a body:
PUT /posts/{id}/read?currentUser={loginId}
Trying to document it using Spring REST Docs 2.0.0.RELEASE I noticed that http-request.adoc is as below:
[source,http,options="nowrap"]
----
PUT /posts/42?currentUser=johndoe HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
currentUser=johndoe
----
I am confused, why currentUser=johndoe is rendered in body (like form parameter)? Is it a bug? Complete example of application below:
#RestController
#RequestMapping("/posts")
#SpringBootApplication
public class DemoApplication {
#PutMapping("{id}/read")
public void markPostRead(#PathVariable("id") String id, #RequestParam("currentUser") String login) {
System.out.println("User " + login + " has read post " + id);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#RunWith(SpringRunner.class)
#WebMvcTest
public class DemoApplicationTests {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void contextLoads() throws Exception {
mockMvc.perform(
RestDocumentationRequestBuilders.put("/posts/{id}/read?currentUser=johndoe", 42))
.andDo(document("mark-as-read", pathParameters(
parameterWithName("id").description("ID of the Post")
)))
.andDo(document("mark-as-read", requestParameters(
parameterWithName("currentUser").description("Login ID of user")
)));
}
}
If you study RFC 2616 with special attention on the PUT section you will read…
The PUT method requests that the enclosed entity be stored under the supplied Request-URI.
So the question is, although the spec is not 100 % clear in this part: Is it allowed to send a PUT request with no request body at all?
This is one of the questions on which you get 11 answers if you ask 10 developers but just to be fine with all imponderables, Spring REST Docs puts your query parameter in the request body just to be prepared for all those little more strict web servers out there. ;-)
Also, according to your endpoint "#PutMapping("{id}/read")", I noticed that in your REST documentation test the "/read" part of the path is missing.

Jersey Test Framework 2.5 - test POST method

I'm trying to find some manual how to test POST methods using jersey framework, only got examples for GET method.
Here's example:
#POST
#Path("add")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response addUser(JAXBElement<User> user) {
int code = userService.addUser(user.getValue());
if (code == 500) {
return Response.status(500).build();
}
return Response.status(code).entity(user).build();
}
Could you please post some POST method test example?
Thank you in advance.
After research I did it!
Here's my solution, it works just fine.
And it's rather integration test, but we can write unit tests in similar manner.
public class RestTest extends JerseyTest{
#Override
protected Application configure() {
return new Your_Resource_Config(); //Your resource config with registered classes
}
//#Before and/or #After for db preparing etc. - if you want integration tests
#Test
public void addUserTest() {
User user = new User();
user.setEmail("user2#mail.com");
user.setName("Jane Doe");
user.getUserRoles().getRoles().add("supertester");
Entity<User> userEntity = Entity.entity(user, MediaType.APPLICATION_XML_TYPE);
target("users/add").request().post(userEntity); //Here we send POST request
Response response = target("users/find").queryParam("email", "user2#mail.com").request().get(); //Here we send GET request for retrieving results
Assert.assertEquals("user2#mail.com", response.readEntity(User.class).getEmail());
}

Categories

Resources