Push data to Soap webservice with Spring - java

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);

Related

Dependency Injection : Could not initialize class org.springframework.beans.CachedIntrospectionResults

I'm developping a Java library that uses Spring framework.
On this library I created a class that will act as application manager. It uses the singleton design pattern.
This class uses dependency injection for external HTTP calls.
public class SharePointManager {
private static SharePointManager instance = null;
private static IAuthenticationResult iAuthenticationResult;
private static String token;
private static SiteService siteService;
public static SharePointManager getInstance() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
SharePointManager.dummyService = context.getBean(DummyService.class);
SharePointManager.siteService = context.getBean(SiteService.class);
if(instance == null) {
instance = new SharePointManager();
}
return instance;
}
private SharePointManager() {
}
public SharePointSiteResponse getAllSites(SharePointCredentialRequest creds) throws Exception {
if( iAuthenticationResult != null && iAuthenticationResult.accessToken() != null ) {
token = iAuthenticationResult.accessToken();
if (Utils.checkToken(iAuthenticationResult.accessToken())) {
token = Utils.getToken(creds).accessToken();
}
} else {
token = Utils.getToken(creds).accessToken();
}
token = iAuthenticationResult.accessToken();
return siteService.getAllSites(creds, token);
}
}
In my Service layer I also want to do dependency injection with a HttpRequest class :
#Service
public class SiteServiceImpl implements SiteService {
private final HttpRequest httpRequest;
public SiteServiceImpl(HttpRequest httpRequest) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
this.httpRequest = context.getBean(HttpRequest.class);
}
#Override
public SharePointSiteResponse getAllSites(SharePointCredentialRequest credentialRequest, String token) throws Exception {
if(Utils.checkToken(token))
token = Utils.createToken(credentialRequest);
URL url = new URL("https://graph.microsoft.com/v1.0/sites?search=*");
return httpRequest.getAllSitesRequest(token, url);
}
}
In my httpRequest class i just build my calls:
#Component
public class HttpRequest {
private final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH").disableHtmlEscaping().create();
public SharePointSiteResponse getAllSitesRequest(String token, URL url)
throws IOException, RequestException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
return getSiteConnection(token, url, conn);
}
....
My AppConfig class is just here for configuration:
#Configuration
#ComponentScan(basePackages = {"fr.dsidiff.sharepoint"})
public class AppConfig {
}
I obtain an error message :
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.springframework.beans.CachedIntrospectionResults
at org.springframework.context.support.AbstractApplicationContext.resetCommonCaches(AbstractApplicationContext.java:969)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
at fr.dsidiff.sharepoint.SharePointManager.getInstance(SharePointManager.java:20)
I tried two solutions that solved my issue in my service layer:
#Service
public class SiteServiceImpl implements SiteService {
private final HttpRequest httpRequest;
#Autowired
public SiteServiceImpl(HttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
...
I also tried javax #Inject too and it works
I didn't expect i could inject using autowired...

OAuth2Client return the same token every time

I have the AuthorizationServer. Besides standard functions i have controller who let to create user. After successful user creates the method must to return token for this user. The problem is that the method return valid token only at first call. At next calls - following users will get the first user's token. I tried to set scope(request) for restTemplate - but obtained the error: " Scope 'request' is not active for the current thread"
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
...
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
...
}
protected ResourceOwnerPasswordResourceDetails getOwnerPasswordResource(){
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
List scopes = new ArrayList<String>(3);
scopes.add(SCOPE_READ);
scopes.add(SCOPE_WRITE);
scopes.add(SCOPE_TRUST);
resource.setAccessTokenUri(tokenUrl);
resource.setClientId(CLIENT_ID);
resource.setClientSecret(CLIENT_SECRET_UNCODED);
resource.setGrantType(GRANT_TYPE_PASSWORD);
resource.setScope(scopes);
return resource;
}
}
Here the OAuth2Client:
#EnableOAuth2Client
#Configuration
public class ClientConfig {
#Autowired
AuthorizationServerConfig authorizationServerConfig;
#Bean
//#Scope("request")
public OAuth2RestOperations restTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(authorizationServerConfig.getOwnerPasswordResource(), new DefaultOAuth2ClientContext(atr));
}
}
And my controller:
#RestController
public class UserRestController {
#Autowired
private OAuth2RestOperations restTemplate;
#PostMapping("/user")
public OAuth2AccessToken createUserCredential(#RequestBody UserCredential user) {
user.validate();
userCredentialService.checkAndSaveUser(user, getClientIp(request));
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("username", user.getLogin());
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("password", user.getPassword);
return restTemplate.getAccessToken();
}
}
May be exists more correct way to obtain token inside of AuthorizationServer ?
I thought have some special way.. but not found it. And solved problem on following way
#EnableOAuth2Client
#Configuration
public class OAuthClientConfig {
#Autowired
AuthorizationServerConfig authorizationServerConfig;
public OAuth2RestOperations restTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(authorizationServerConfig.getOwnerPasswordResource(), new DefaultOAuth2ClientContext(atr));
}
}
And my controller:
#RestController
public class UserRestController {
#Autowired
private OAuthClientConfig oAuthClientConfig;
#PostMapping("/user")
public OAuth2AccessToken createUserCredential(#RequestBody UserCredential user) {
user.validate();
userCredentialService.checkAndSaveUser(user, getClientIp(request));
OAuth2RestOperations restTemplate = oAuthClientConfig.restTemplate();
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("username", user.getLogin());
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("password", user.getPassword);
return restTemplate.getAccessToken();
}
}
May be it will help to someone
I was facing the same issue I found this other way to make it work
#Bean
#Primary
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext context,
OAuth2ProtectedResourceDetails details) {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
OAuth2RestTemplate template = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider>asList(
new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));
template.setAccessTokenProvider(accessTokenProvider);
return template;
}
And then I just did the injection
private final OAuth2RestTemplate oauth2RestTemplate;
#GetMapping(path = "/token")
public String token(Credentials credentials) {
oauth2RestTemplate.getOAuth2ClientContext()
.getAccessTokenRequest().add("username", credentials.getEmail());
oauth2RestTemplate.getOAuth2ClientContext()
.getAccessTokenRequest().add("password", credentials.getPass());
final OAuth2AccessToken accessToken = oauth2RestTemplate.getAccessToken();
final String accessTokenAsString = accessToken.getValue();
return accessTokenAsString ;
}

Access springbeans in JerseyTest with Jersey2.0

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.

Can't upload multipart file with mockMvc [duplicate]

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()));
}
}

How to login a user with spring 3.2 new mvc testing

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)

Categories