I have jersey project with spring. Now My test is deriverd from JerseyTest. When I try to do
#AutoWired
RestTemplate restTemplate;
It looks like spring is not working in jersey test. I did some research and found some links like
spring_jersey
but it did not work ,since I am using jersey2.0.
My code looks like
//AbstractTest
package com.test;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Application;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.validation.ValidationFeature;
public abstract class AbstractTest extends JerseyTest
{
protected WebTarget getRootTarget(final String rootResource)
{
return client().target(getBaseUri()).path(rootResource);
}
#Override
protected final Application configure()
{
final ResourceConfig application = configureApplication();
// needed for json serialization
application.register(JacksonFeature.class);
// bean validation
application.register(ValidationFeature.class);
// configure spring context
application.property("contextConfigLocation", "classpath:/META-INF/applicationContext.xml");
// disable bean validation for tests
application.property(ServerProperties.BV_FEATURE_DISABLE, "true");
return application;
}
protected abstract ResourceConfig configureApplication();
#Override
protected void configureClient(final ClientConfig config)
{
// needed for json serialization
config.register(JacksonFeature.class);
config.register(new LoggingFilter(java.util.logging.Logger.getLogger(AbstractResourceTest.class.getName()), false));
super.configureClient(config);
}
}
package com.test;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
//MyTest
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import org.apache.commons.io.IOUtils;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import org.springframework.web.client.RestTemplate;
import junit.framework.Assert;
public final class MyTest extends AbstractTest
{
private static final String ROOT_RESOURCE_PATH = "/testUrl";
#AutoWired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Before
public void setup(){
this.restTemplate = new RestTemplate();
this.mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testPostWithString() {
WebTarget target = getRootTarget(ROOT_RESOURCE_PATH).path("");
String entityBody = new String();
entityBody = " My test data";
final javax.ws.rs.client.Entity<String> entity = javax.ws.rs.client.Entity.entity(entityBody, "text/plain");
mockServer.expect(MockRestRequestMatchers.requestTo(ROOT_RESOURCE_PATH)).andExpect(method(HttpMethod.POST)).andExpect(content().string(entityBody))
.andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));
final Response response = target.request().post(entity);
Assert.assertNotNull("Response must not be null", response.getEntity());
Assert.assertEquals("Response does not have expected response code", 200, response.getStatus());
System.out.println("Response = " + response.getEntity());
String data = response.readEntity(String.class);
System.out.println("Response = " + data);
if(response.ok() != null)
{
System.out.println("Ok");
}
}
}
Update:
public class SimpleJerseyTest extends ApplicationContextAwareJerseyTest {
private static final String ROOT_RESOURCE_PATH = "/test";
#Override
public void configureApplication(ResourceConfig config) {
config.register(MyApp.class);
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
#Before
public void setUp() {
try{
((ConfigurableApplicationContext)this.applicationContext).refresh();
super.setUp();
}catch(Exception e){
}
this.mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Test
public void doitOnce() {
WebTarget target = target(ROOT_RESOURCE_PATH);
String entityBody = new String();
final javax.ws.rs.client.Entity<String> entity = javax.ws.rs.client.Entity.entity(entityBody, "text/plain");
mockServer.expect(MockRestRequestMatchers.requestTo(ROOT_RESOURCE_PATH)).andExpect(method(HttpMethod.POST)).andExpect(content().string(entityBody))
.andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));
final Response response = target.request().post(entity);
System.out.println("Response = " + response.getEntity());
String data = response.readEntity(String.class);
System.out.println("Response = " + data);
if(response.ok() != null)
{
System.out.println("Ok");
}
}
}
I have added bean in
src/test/resources/META-INF/applicationContext.xml
<!-- Our REST Web Service client -->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>
Same bean I have added in
src/main/resources/META-INF/applicationContext.xml
!-- Our REST Web Service client -->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>
Instead of configuring Spring like this
application.property("contextConfigLocation", "classpath:/META-INF/applicationContext.xml");
You can instead use
application.property("contextConfig", <ApplicationContext>);
This way you can have an instance of the ApplicationContext, where you can get the AutowireCapableBeanFactory. With this, you can just call acbf.autowireBean(this) to inject the test class.
Here's what I mean. I tested it, and it works find for simple cases. Won't work well though if the beans you are trying to inject aren't singletons, as new one will be create for the test and where ever else you try to inject to in your application code
public abstract class ApplicationContextAwareJerseyTest extends JerseyTest {
protected ApplicationContext applicationContext;
#Override
protected final ResourceConfig configure() {
final ResourceConfig config = new ResourceConfig();
configureApplication(config);
this.applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
config.property("contextConfig", this.applicationContext);
final AutowireCapableBeanFactory bf = this.applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
return config;
}
public final ApplicationContext getApplicationContext() {
return this.applicationContext;
}
protected void configureApplication(ResourceConfig resourceConfig) {};
}
One thing I am not sure about though is how the resetting would work. I tried to add
#Before
public void setUp() throws Exception {
((ConfigurableApplicationContext)this.applicationContext).refresh();
super.setUp();
}
into the abstract class, but that doesn't seem to work as expected. The test I used is as follows
public class SimpleJerseyTest extends ApplicationContextAwareJerseyTest {
#Path("test")
public static class SimpleResource {
#Autowired
private MessageService service;
#GET
public String getMessage() {
return this.service.getMessage();
}
}
#Override
public void configureApplication(ResourceConfig config) {
config.register(SimpleResource.class);
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
#Before
public void before() {
assertEquals("Hello World", messageService.getMessage());
}
#Autowired
private MessageService messageService;
#Test
public void doitOnce() {
messageService.setMessage("BOOYAH");
final Response response = target("test").request().get();
assertEquals("BOOYAH", response.readEntity(String.class));
}
#Test
public void doitTwice() {
messageService.setMessage("BOOYAH");
final Response response = target("test").request().get();
assertEquals("BOOYAH", response.readEntity(String.class));
}
}
The result I would get for the second test is that the value of the service message was the default message "Hello World", even though I set the message to "BOOYAH". This tells me that there is a stale service in the application, which is not the same one being injected into the test. The first test works fine though. Without resetting, the second test would work fine also, but you are left with modified services in each test, which makes the test not self-contained.
Related
I am learning to write Unit Test for SpringBoot Restcontroller , wrote this and test passes
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {FhirApp.class, TestSecurityConfiguration.class})
#AutoConfigureMockMvc
public class ObservationControllerTest {
private ObjectMapper objectMapper = new ObjectMapper();
#Autowired
private MockMvc mockMvc;
#MockBean
private ObservationService observationService;
#Test
public void createObservationResource() throws Exception {
given(observationService.createObservation(ResourceStringProvider.observationsource()))
.willReturn(responseDocument);
String jsonString = objectMapper.writeValueAsString(
ResourceStringProvider.observationsource());
mockMvc.perform(post("/Observation")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonString))
.andExpect(status()
.isOk());
}
But as this and this , i am also getting empty response for response.getContentAsString() :
Mockito.when(observationService.createObservation(Mockito.any())).thenReturn(responseDocument);
String jsonString = objectMapper.writeValueAsString(ResourceStringProvider.observationsource());
MockHttpServletResponse response = mockMvc.perform(post("/Observation")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonString))
.andReturn()
.getResponse();
assertThat(response.getContentAsString())
.isEqualTo(new ObjectMapper()
.writeValueAsString(responseDocument));
I already tried the solutions provided by them :
1: Using Mockito.any(String.class)
2: webEnvironment = SpringBootTest.WebEnvironment.MOCK
3: using thenCallRealMethod instead of thenReturn(responseDocument)
But unfortunately it didn't work,already tried different possibilities , I also tried using MockitoJunitRunner :
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {FhirApp.class, TestSecurityConfiguration.class})
#AutoConfigureMockMvc
public class ObservationControllerTest {
private MockMvc mockMvc;
#Mock
private ObservationService observationService;
#Mock
private RequestFilter requestFilter;
#InjectMocks
private ObservationController observationController;
#Before
public void setup() {
Resource resource = Utility.convertFromStringToFhirResource(Observation.class,ResourceStringProvider.observationResponse());
responseDocument=Utility.convertFromFhirResourceToMongoInsertibleDoc(resource);
//these line enabled for MockitoJUnitRunner only
this.mockMvc = MockMvcBuilders.standaloneSetup(observationController)
.setControllerAdvice(new FhirRuntimeException("Error Happened"))
.addFilters(requestFilter)
.build();
}
#Test
public void createObservationResource()throws Exception{
Mockito.when(observationService.createObservation(ResourceStringProvider.observationsource())).thenReturn(responseDocument);
String jsonString = objectMapper.writeValueAsString(ResourceStringProvider.observationsource());
MockHttpServletResponse response = mockMvc.perform(
post("/Observation")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonString))
.andReturn()
.getResponse();
assertThat(response.getContentAsString()).isEqualTo(new ObjectMapper().writeValueAsString(responseDocument));
}
i think since not many have went through such issues, issue isn't much talked about . What could be the reason of empty response when response status is ok?
Controller code :
#RestController
#RequestMapping("/api")
public class ObservationController {
#Autowired
private ObservationService observationService;
#GetMapping("/Observation/{id}")
public ResponseEntity<Document> getObservationByID(
#RequestParam("_pretty") Optional<String> pretty,
#PathVariable("id") String id) {
Document resultDoc = observationService.getObservationById(id);
return new ResponseEntity<>(resultDoc, HttpStatus.OK);
}
#PostMapping(path = "/Observation", consumes = {"application/json", "application/fhir+json"},
produces = {"application/json", "application/fhir+json"})
public ResponseEntity<Document> createObservationResource(#RequestBody String fhirResource) {
Document fhirDoc = observationService.createObservation(fhirResource);
return new ResponseEntity<>(fhirDoc,
Utility.createHeaders(fhirDoc),
HttpStatus.CREATED);
}
//other methods
}
I realized the call to post in test should be /api/Observation , but it didn't make any difference . Thanks in Advance.
Posting the complete answer alongwith imports here so that it will help someone (actually we don't require any annotation above and it is one way to run it , during my findings i came accross this post which helps in solving design issue which may occur if you go ahead with #InjectMocks approach)
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import com.comitemd.emr.datalayer.fhir.service.ObservationService;
import com.comitemd.emr.datalayer.fhir.utility.ResourceStringProvider;
import com.comitemd.emr.datalayer.fhir.utility.Utility;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bson.Document;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Resource;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
//#RunWith(MockitoJUnitRunner.class)
public class ObservationControllerStandaloneTest {
private MockMvc mockMvc;
#Mock
private ObservationService observationService;
#InjectMocks
private ObservationController observationController;
private Document responseDocument;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);// enable this or MockitoJUnitRunner.class
Resource resource = Utility
.convertFromStringToFhirResource(Observation.class, ResourceStringProvider.observationResponse());
responseDocument=Utility.convertFromFhirResourceToMongoInsertibleDoc(resource);
this.mockMvc = MockMvcBuilders.standaloneSetup(observationController).build();
}
#Test
public void createObservationResource()throws Exception{
Mockito.when(observationService.createObservation(ResourceStringProvider.observationsource()))
.thenReturn(responseDocument);
MockHttpServletResponse response = mockMvc.perform(
post("/api/Observation")
.contentType(MediaType.APPLICATION_JSON)
.content(ResourceStringProvider.observationsource()))
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse();
verify(observationService, times(1)).createObservation(Mockito.any());
assertThat(response.getContentAsString()).isEqualTo(new ObjectMapper().writeValueAsString(responseDocument));
}
}
I have the following request handler for saving autos. I have verified that this works when I use e.g. cURL. Now I want to unit test the method with Spring MVC Test. I have tried to use the fileUploader, but I am not managing to get it working. Nor do I manage to add the JSON part.
How would I unit test this method with Spring MVC Test? I am not able to find any examples on this.
#RequestMapping(value = "autos", method = RequestMethod.POST)
public ResponseEntity saveAuto(
#RequestPart(value = "data") autoResource,
#RequestParam(value = "files[]", required = false) List<MultipartFile> files) {
// ...
}
I want to uplod a JSON representation for my auto + one or more files.
I will add 100 in bounty to the correct answer!
Since MockMvcRequestBuilders#fileUpload is deprecated, you'll want to use MockMvcRequestBuilders#multipart(String, Object...) which returns a MockMultipartHttpServletRequestBuilder. Then chain a bunch of file(MockMultipartFile) calls.
Here's a working example. Given a #Controller
#Controller
public class NewController {
#RequestMapping(value = "/upload", method = RequestMethod.POST)
#ResponseBody
public String saveAuto(
#RequestPart(value = "json") JsonPojo pojo,
#RequestParam(value = "some-random") String random,
#RequestParam(value = "data", required = false) List<MultipartFile> files) {
System.out.println(random);
System.out.println(pojo.getJson());
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
return "success";
}
static class JsonPojo {
private String json;
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
}
}
and a unit test
#WebAppConfiguration
#ContextConfiguration(classes = WebConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class Example {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void test() throws Exception {
MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some xml".getBytes());
MockMultipartFile secondFile = new MockMultipartFile("data", "other-file-name.data", "text/plain", "some other type".getBytes());
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", "{\"json\": \"someValue\"}".getBytes());
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders.multipart("/upload")
.file(firstFile)
.file(secondFile)
.file(jsonFile)
.param("some-random", "4"))
.andExpect(status().is(200))
.andExpect(content().string("success"));
}
}
And the #Configuration class
#Configuration
#ComponentScan({ "test.controllers" })
#EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
The test should pass and give you output of
4 // from param
someValue // from json file
filename.txt // from first file
other-file-name.data // from second file
The thing to note is that you are sending the JSON just like any other multipart file, except with a different content type.
The method MockMvcRequestBuilders.fileUpload is deprecated use MockMvcRequestBuilders.multipart instead.
This is an example:
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;
/**
* Unit test New Controller.
*
*/
#RunWith(SpringRunner.class)
#WebMvcTest(NewController.class)
public class NewControllerTest {
private MockMvc mockMvc;
#Autowired
WebApplicationContext wContext;
#MockBean
private NewController newController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wContext)
.alwaysDo(MockMvcResultHandlers.print())
.build();
}
#Test
public void test() throws Exception {
// Mock Request
MockMultipartFile jsonFile = new MockMultipartFile("test.json", "", "application/json", "{\"key1\": \"value1\"}".getBytes());
// Mock Response
NewControllerResponseDto response = new NewControllerDto();
Mockito.when(newController.postV1(Mockito.any(Integer.class), Mockito.any(MultipartFile.class))).thenReturn(response);
mockMvc.perform(MockMvcRequestBuilders.multipart("/fileUpload")
.file("file", jsonFile.getBytes())
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
}
Have a look at this example taken from the spring MVC showcase, this is the link to the source code:
#RunWith(SpringJUnit4ClassRunner.class)
public class FileUploadControllerTests extends AbstractContextControllerTests {
#Test
public void readString() throws Exception {
MockMultipartFile file = new MockMultipartFile("file", "orig", null, "bar".getBytes());
webAppContextSetup(this.wac).build()
.perform(fileUpload("/fileupload").file(file))
.andExpect(model().attribute("message", "File 'orig' uploaded successfully"));
}
}
Here's what worked for me, here I'm attaching a file to my EmailController under test. Also take a look at the postman screenshot on how I'm posting the data.
#WebAppConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = EmailControllerBootApplication.class
)
public class SendEmailTest {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void testSend() throws Exception{
String jsonStr = "{\"to\": [\"email.address#domain.com\"],\"subject\": "
+ "\"CDM - Spring Boot email service with attachment\","
+ "\"body\": \"Email body will contain test results, with screenshot\"}";
Resource fileResource = new ClassPathResource(
"screen-shots/HomePage-attachment.png");
assertNotNull(fileResource);
MockMultipartFile firstFile = new MockMultipartFile(
"attachments",fileResource.getFilename(),
MediaType.MULTIPART_FORM_DATA_VALUE,
fileResource.getInputStream());
assertNotNull(firstFile);
MockMvc mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders
.multipart("/api/v1/email/send")
.file(firstFile)
.param("data", jsonStr))
.andExpect(status().is(200));
}
}
If you are using Spring4/SpringBoot 1.x, then it's worth mentioning that you can add "text" (json) parts as well . This can be done via MockMvcRequestBuilders.fileUpload().file(MockMultipartFile file) (which is needed as method .multipart() is not available in this version):
#Test
public void test() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.fileUpload("/files")
// file-part
.file(makeMultipartFile( "file-part" "some/path/to/file.bin", "application/octet-stream"))
// text part
.file(makeMultipartTextPart("json-part", "{ \"foo\" : \"bar\" }", "application/json"))
.andExpect(status().isOk())));
}
private MockMultipartFile(String requestPartName, String filename,
String contentType, String pathOnClassPath) {
return new MockMultipartFile(requestPartName, filename,
contentType, readResourceFile(pathOnClasspath);
}
// make text-part using MockMultipartFile
private MockMultipartFile makeMultipartTextPart(String requestPartName,
String value, String contentType) throws Exception {
return new MockMultipartFile(requestPartName, "", contentType,
value.getBytes(Charset.forName("UTF-8")));
}
private byte[] readResourceFile(String pathOnClassPath) throws Exception {
return Files.readAllBytes(Paths.get(Thread.currentThread().getContextClassLoader()
.getResource(pathOnClassPath).toUri()));
}
}
Hi I've referred to this link for consuming a SOAP webservice.
But i'm not sure how to call the client method.
Please find my code below :
ClientConfig.java
package com.exclusively.unicommerce.service;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class ClientConfig {
#Bean
public Jaxb2Marshaller marshaller()
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.unicommerce.wsdl");
return marshaller;
}
#Bean
public SaleOrderClient saleorderclient(Jaxb2Marshaller marshaller) {
SaleOrderClient client = new SaleOrderClient();
client.setDefaultUri("https://link.com/services/soap/?version=1.6");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
SaleOrderClient.java
public class SaleOrderClient extends WebServiceGatewaySupport{
private static final String uri = "https://link.com/services/soap/?version=1.6";
public String createSaleOrder(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder = setSaleOrderObject(suborder);
CreateSaleOrderRequest request = new CreateSaleOrderRequest();
request.setSaleOrder(saleorder);
//PLEASE NOTE THIS Line of CODE.
this.getWebServiceTemplate().marshalSendAndReceive(uri,request);
return "Pushed to Unicommerce";
}
public SaleOrder setSaleOrderObject(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder.setAdditionalInfo(null);
saleorder.setAddresses(null);
saleorder.setCashOnDelivery(null);
saleorder.setCFormProvided(null);
saleorder.setChannel(null);
saleorder.setCode(null);
saleorder.setCurrencyCode(null);
saleorder.setCustomerCode(null);
saleorder.setDisplayOrderCode(null);
saleorder.setNotificationEmail(null);
saleorder.setNotificationMobile(null);
saleorder.setVerificationRequired(null);
return saleorder;
}
}
SuborderController.java
#Controller
public class SuborderController {
private String currentStatus, finalStatus,status,response;
#Autowired
private SuborderService suborderservice;
#RequestMapping(value = "/orders/add", method = RequestMethod.POST)
#ResponseBody
public String addOrders(#RequestBody Suborder order) {
if(order.getSuborderId() == null || order.getSuborderId().isEmpty())
return "BAD REQUEST";
suborderservice.addOrders(order);
//**CALL To createSaleorder(order)**
//response = saleorderclient.createSaleorder(order);
return response;
}
Here things to note is that webservice has provided request class but no response class. Second i tried
#Autowired
SaleOrderClient saleorderclient;
But it threw bean not found exception.
I'm not able to understand how do i access this method.
Please help. TIA.
I resolved my issue by adding below mentioned lines in SuborderController.java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ClientConfig.class);
ctx.refresh();
SaleOrderClient saleorderclient = ctx.getBean(SaleOrderClient.class);
I'm injecting my cookie param in that way (using javax.ws.rs.CookieParam)
#CookieParam("parameterCookie")
private String parameterCookie;
i have a problem to inject that parameter using Resteasy
ERROR
It is illegal to inject a #CookieParam into a singleton
That is a BaseResource and i can't modify all resources to accept that paramenter on all methods (it costs a lot). How could i inject that CookieParam in Resteasy without modify all resources?
You can work around this by injecting HttpHeaders instead:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
public class CookieTest {
static final String COOKIE_NAME = "parameterCookie";
Dispatcher dispatcher;
#Before
public void setUp() throws Exception {
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(new Resource());
}
#Test
public void name_StateUnderTest_ExpectedBehavior() throws Exception {
String cookieValue = String.valueOf(System.currentTimeMillis());
MockHttpResponse response = new MockHttpResponse();
MockHttpRequest request = MockHttpRequest.get("/")
.cookie(COOKIE_NAME, cookieValue);
dispatcher.invoke(request, response);
assertThat(response.getContentAsString(), is(COOKIE_NAME + "=" + cookieValue));
}
#Path("/")
public static class Resource {
#Context HttpHeaders headers;
#GET #Path("/")
public String getCookie(){
return headers.getCookies().get(COOKIE_NAME).toString();
}
}
}
This works fine until I have to test a service that needs a logged in user, how do I add user to context :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:applicationContext-test.xml")
#WebAppConfiguration
public class FooTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Resource(name = "aService")
private AService aService; //uses logged in user
#Before
public void setup() {
this.mockMvc = webAppContextSetup(this.webApplicationContext).build();
}
If you want to use MockMVC with the latest spring security test package, try this code:
Principal principal = new Principal() {
#Override
public String getName() {
return "TEST_PRINCIPAL";
}
};
getMockMvc().perform(get("http://your-url.com").principal(principal))
.andExpect(status().isOk()));
Keep in mind that you have to be using Principal based authentication for this to work.
If successful authentication yields some cookie, then you can capture that (or just all cookies), and pass it along in the next tests:
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy filterChain;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(filterChain).build();
}
#Test
public void testSession() throws Exception {
// Login and save the cookie
MvcResult result = mockMvc.perform(post("/session")
.param("username", "john").param("password", "s3cr3t")).andReturn();
Cookie c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), greaterThan(10));
// No cookie; 401 Unauthorized
mockMvc.perform(get("/personal").andExpect(status().isUnauthorized());
// With cookie; 200 OK
mockMvc.perform(get("/personal").cookie(c)).andExpect(status().isOk());
// Logout, and ensure we're told to wipe the cookie
result = mockMvc.perform(delete("/session").andReturn();
c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), is(0));
}
Though I know I'm not making any HTTP requests here, I kind of like the stricter separation of the above integration test and my controllers and Spring Security implementation.
To make the code a bit less verbose, I use the following to merge the cookies after making each request, and then pass those cookies along in each subsequent request:
/**
* Merges the (optional) existing array of Cookies with the response in the
* given MockMvc ResultActions.
* <p>
* This only adds or deletes cookies. Officially, we should expire old
* cookies. But we don't keep track of when they were created, and this is
* not currently required in our tests.
*/
protected static Cookie[] updateCookies(final Cookie[] current,
final ResultActions result) {
final Map<String, Cookie> currentCookies = new HashMap<>();
if (current != null) {
for (Cookie c : current) {
currentCookies.put(c.getName(), c);
}
}
final Cookie[] newCookies = result.andReturn().getResponse().getCookies();
for (Cookie newCookie : newCookies) {
if (StringUtils.isBlank(newCookie.getValue())) {
// An empty value implies we're told to delete the cookie
currentCookies.remove(newCookie.getName());
} else {
// Add, or replace:
currentCookies.put(newCookie.getName(), newCookie);
}
}
return currentCookies.values().toArray(new Cookie[currentCookies.size()]);
}
...and a small helper as cookie(...) needs at least one cookie:
/**
* Creates an array with a dummy cookie, useful as Spring MockMvc
* {#code cookie(...)} does not like {#code null} values or empty arrays.
*/
protected static Cookie[] initCookies() {
return new Cookie[] { new Cookie("unittest-dummy", "dummy") };
}
...to end up with:
Cookie[] cookies = initCookies();
ResultActions actions = mockMvc.perform(get("/personal").cookie(cookies)
.andExpect(status().isUnauthorized());
cookies = updateCookies(cookies, actions);
actions = mockMvc.perform(post("/session").cookie(cookies)
.param("username", "john").param("password", "s3cr3t"));
cookies = updateCookies(cookies, actions);
actions = mockMvc.perform(get("/personal").cookie(cookies))
.andExpect(status().isOk());
cookies = updateCookies(cookies, actions);
You should be able to just add the user to the security context:
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new GrantedAuthorityImpl("ROLE_USER"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, password,list);
SecurityContextHolder.getContext().setAuthentication(auth);
Somewhy solution with principal didn't worked for me, thus, I'd like to mention another way out:
mockMvc.perform(get("your/url/{id}", 5).with(user("anyUserName")))
With spring 4 this solution mock the formLogin and logout using sessions and not cookies because spring security test not returning cookies.
Because it's not a best practice to inherit tests you can #Autowire this component in your tests and call it's methods.
With this solution each perform operation on the mockMvc will be called as authenticated if you called the performLogin on the end of the test you can call performLogout.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.Filter;
import static com.condix.SessionLogoutRequestBuilder.sessionLogout;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#Component
public class SessionBasedMockMvc {
private static final String HOME_PATH = "/";
private static final String LOGOUT_PATH = "/login?logout";
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Filter springSecurityFilterChain;
private MockMvc mockMvc;
public MockMvc createSessionBasedMockMvc() {
final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path");
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.defaultRequest(defaultRequestBuilder)
.alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest()))
.apply(springSecurity(springSecurityFilterChain))
.build();
return this.mockMvc;
}
public void performLogin(final String username, final String password) throws Exception {
final ResultActions resultActions = this.mockMvc.perform(formLogin().user(username).password(password));
this.assertSuccessLogin(resultActions);
}
public void performLogout() throws Exception {
final ResultActions resultActions = this.mockMvc.perform(sessionLogout());
this.assertSuccessLogout(resultActions);
}
private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder,
final MockHttpServletRequest request) {
requestBuilder.session((MockHttpSession) request.getSession());
return request;
}
private void assertSuccessLogin(final ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isFound())
.andExpect(authenticated())
.andExpect(redirectedUrl(HOME_PATH));
}
private void assertSuccessLogout(final ResultActions resultActions) throws Exception {
resultActions.andExpect(status().isFound())
.andExpect(unauthenticated())
.andExpect(redirectedUrl(LOGOUT_PATH));
}
}
Because default LogoutRequestBuilder doesn't support session we need to create another logout request builder.
import org.springframework.beans.Mergeable;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
/**
* This is a logout request builder which allows to send the session on the request.<br/>
* It also has more than one post processors.<br/>
* <br/>
* Unfortunately it won't trigger {#link org.springframework.security.core.session.SessionDestroyedEvent} because
* that is triggered by {#link org.apache.catalina.session.StandardSessionFacade#invalidate()} in Tomcat and
* for mocks it's handled by #{{#link MockHttpSession#invalidate()}} so the log out message won't be visible for tests.
*/
public final class SessionLogoutRequestBuilder implements
ConfigurableSmartRequestBuilder<SessionLogoutRequestBuilder>, Mergeable {
private final List<RequestPostProcessor> postProcessors = new ArrayList<>();
private String logoutUrl = "/logout";
private MockHttpSession session;
private SessionLogoutRequestBuilder() {
this.postProcessors.add(csrf());
}
static SessionLogoutRequestBuilder sessionLogout() {
return new SessionLogoutRequestBuilder();
}
#Override
public MockHttpServletRequest buildRequest(final ServletContext servletContext) {
return post(this.logoutUrl).session(session).buildRequest(servletContext);
}
public SessionLogoutRequestBuilder logoutUrl(final String logoutUrl) {
this.logoutUrl = logoutUrl;
return this;
}
public SessionLogoutRequestBuilder session(final MockHttpSession session) {
Assert.notNull(session, "'session' must not be null");
this.session = session;
return this;
}
#Override
public boolean isMergeEnabled() {
return true;
}
#SuppressWarnings("unchecked")
#Override
public Object merge(final Object parent) {
if (parent == null) {
return this;
}
if (parent instanceof MockHttpServletRequestBuilder) {
final MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent;
if (this.session == null) {
this.session = (MockHttpSession) ReflectionTestUtils.getField(parentBuilder, "session");
}
final List postProcessors = (List) ReflectionTestUtils.getField(parentBuilder, "postProcessors");
this.postProcessors.addAll(0, (List<RequestPostProcessor>) postProcessors);
} else if (parent instanceof SessionLogoutRequestBuilder) {
final SessionLogoutRequestBuilder parentBuilder = (SessionLogoutRequestBuilder) parent;
if (!StringUtils.hasText(this.logoutUrl)) {
this.logoutUrl = parentBuilder.logoutUrl;
}
if (this.session == null) {
this.session = parentBuilder.session;
}
this.postProcessors.addAll(0, parentBuilder.postProcessors);
} else {
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
}
return this;
}
#Override
public SessionLogoutRequestBuilder with(final RequestPostProcessor postProcessor) {
Assert.notNull(postProcessor, "postProcessor is required");
this.postProcessors.add(postProcessor);
return this;
}
#Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
for (final RequestPostProcessor postProcessor : this.postProcessors) {
request = postProcessor.postProcessRequest(request);
if (request == null) {
throw new IllegalStateException(
"Post-processor [" + postProcessor.getClass().getName() + "] returned null");
}
}
return request;
}
}
After calling the performLogin operation all your request in the test will be automatically performed as logged in user.
Yet another way... I use the following annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TestExecutionListeners(listeners={ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExcecutionListener.class})
#WithMockUser
public class WithMockUserTests {
...
}
(source)