JAX-RS DynamicFeature implementation not called from VertxResteasyDeployment - java

I'm trying to Log unique request id for each http request using MDC to my resource for debugging and trace a particular request. For same I've created one custom annotation #TagRequestID. Below is the for logging request id. But what I'm not able to achieve the request is not going via DynamicFilter implementation what I think there should be some method or way in VertxResteasyDeployment class which should help to resolve this.
Basically the request is not going via filter even I tried with setProviders method of VertxResteasyDeployment class. Can someone please guide what I'm missing here ? I believe there should be some config which request to pass request via DynamicFeature implementation where we inject RequestIdConfig bean. (Assume I've RequestIdConfig bean created).
#TagRequestID code :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TagRequestID {
}
custom class RequestIdConfig :
import lombok.Data;
import javax.validation.constraints.NotNull;
#Data
public class RequestIdConfig {
#NotNull
private String resourcePackage;
#NotNull
private String requestIdHeaderKey;
#NotNull
private Boolean requestIdMandatoryFlag;
}
custom class RequestIdFeature :
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
#Slf4j
#AllArgsConstructor
#Provider
public class RequestIdFeature
implements DynamicFeature {
RequestIdConfig requestIdConfig;
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) {
log.info("testing now===");
final Class<?> resourceClass = resourceInfo.getResourceClass();
//Check if the current resource is to validated
if (resourceClass.getPackage().getName().startsWith(requestIdConfig.getResourcePackage())) {
//Check if the Validation annotation is present
if (resourceInfo.getResourceMethod().getAnnotation(TagRequestID.class) != null) {
log.info(resourceInfo.getResourceMethod() + " registered for clientID validation");
featureContext.register(new RequestIdFilter(requestIdConfig, resourceInfo));
}
}
}
}
custom class for filter :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.MDC;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;
#Data
#AllArgsConstructor
#Slf4j
#Provider
public class RequestIdFilter
implements ContainerRequestFilter {
RequestIdConfig requestIdConfig;
ResourceInfo resourceInfo;
public static final String REQUEST_ID = "request-Id";
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
log.info("ClientIdValidation filter method invoked");
Method resourceMethod = resourceInfo.getResourceMethod();
// Validate Clients
validatePermissions(containerRequestContext);
}
private void validatePermissions(final ContainerRequestContext containerRequestContext) {
String requestId = containerRequestContext.getHeaderString(requestIdConfig.getRequestIdHeaderKey());
//Make sure the Header key is present if mandatory flag is true
if (requestIdConfig.getRequestIdMandatoryFlag() && StringUtils.isAnyEmpty(requestId)) {
throw new WebApplicationException(requestIdConfig.getRequestIdHeaderKey() + " can't be null", Response.Status.UNAUTHORIZED);
}
//If no request ID present, generate a UUID
if (StringUtils.isAnyEmpty(requestId)) {
requestId = UUID.randomUUID()
.toString();
}
containerRequestContext.setProperty(REQUEST_ID, requestId);
MDC.put(REQUEST_ID, requestId);
}
}
Resource or Controller code :
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.vertx.core.Vertx;
import io.vertx.core.WorkerExecutor;
public class Processor {
#POST
#TagRequestID
#Path("/update_record")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON })
public void updateEvent(String data) throws Exception{
//do something here
}
Server code from where we run this:
import mypackage.Processor;
import io.vertx.core.AbstractVerticle;
import org.jboss.resteasy.plugins.server.vertx.VertxRequestHandler;
import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment;
import org.springframework.context.ApplicationContext;
public class VertxServer extends AbstractVerticle {
VertxServer(final ApplicationContext context) {
}
#Override
public void start() throws Exception {
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
deployment.getRegistry().addPerInstanceResource(Processors.class);
vertx.createHttpServer()
.requestHandler(new VertxRequestHandler(vertx, deployment))
.listen(8080);
}
}
Once server is up and running then just hit two request simultaneously on the above controller . i.e :
curl -X POST \ http://localhost:8080/v1/update_record \ -H
'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \
-H 'Postman-Token: c9494189-4ac9-9f6c-44f6-216186c74431' \ -d '{"id":"123"}'

In VertxServer.java register your dynamicFeature instance in providerFactory before you register your resources. This dynamicFeatures and filters registered in providerFactory will be used while registering resources.
Your code will go like this:
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
deployment.getProviderFactory().register(new RequestIdFeature(getRequiredBean());
deployment.getRegistry().addPerInstanceResource(Processors.class);
vertx.createHttpServer()
.requestHandler(new VertxRequestHandler(vertx, deployment))
.listen(8080);

VertxServer.java
deployment.getProviderFactory().register(RequestIdFilter.class)

Related

Azure spring boot function-how to pass trigger and input binding both to handleRequest method at same time?

package com.att.trace.function;
import java.util.Optional;
import com.att.trace.function.model.User;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.BlobInput;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import com.microsoft.azure.functions.annotation.StorageAccount;
import org.springframework.cloud.function.adapter.azure.FunctionInvoker;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
public class HelloHandler extends FunctionInvoker<Tuple2<User, String>, String> {
#FunctionName("hello")
#StorageAccount("AzureWebJobsStorage")
public HttpResponseMessage execute(#HttpTrigger(name = "request", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request,
#BlobInput(name = "requestjsonblob", dataType = "string", path = "requestjson/{name}.json") String requestjson,
ExecutionContext context) {
User user = request.getBody().get();
System.out.println("requestjson=" + requestjson);
Tuple2<User, String> input = Tuples.of(user, requestjson);
handleRequest(input, context);
return request.createResponseBuilder(HttpStatus.OK).body("ok")
.header("Content-Type", "application/json").build();
}
}
package com.att.trace.function;
import java.util.function.Function;
import com.att.trace.function.model.User;
import org.springframework.stereotype.Component;
import reactor.util.function.Tuple2;
#Component
public class Hello implements Function<Tuple2<User, String>, String> {
public String apply(Tuple2<User, String> objects) {
System.out.println("objects.getT1()=" + objects.getT1());
System.out.println("objects.getT2()=" + objects.getT2());
String output = "test output";
return output;
}
}
getting below exception when I create tuple of Http trigger and blobinput and send it to handleRequest..
[2021-08-20T09:53:12.816Z] java.lang.UnsupportedOperationException: At the moment only Tuple-based function are supporting multiple arguments
[2021-08-20T09:53:12.818Z] at
I need to send both inputs to handleRequest method... any clue how to achieve that?
Spent some time and the only way I could achieve that what you want is by using DTO class.
package com.example.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
public class HelloInputArguments {
private User user;
private String content;
}
Then handler is easy
package com.example.demo;
import com.example.demo.model.HelloInputArguments;
import com.example.demo.model.User;
import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.*;
import org.springframework.cloud.function.adapter.azure.FunctionInvoker;
import java.util.Optional;
public class HelloHandler extends FunctionInvoker<HelloInputArguments, String> {
#FunctionName("hello")
#StorageAccount("DefaultConnection")
public HttpResponseMessage execute(
#HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request,
#BlobInput(name = "file", dataType = "string", path = "requestjson/proam.json") String content,
ExecutionContext context) {
User user = request.getBody().get();
return request.createResponseBuilder(HttpStatus.OK)
.body(handleRequest(new HelloInputArguments(user, content), context))
.header("Content-Type", "application/json")
.build();
}
}
And function
package com.example.demo;
import com.example.demo.model.HelloInputArguments;
import org.springframework.stereotype.Component;
import java.util.function.Function;
#Component
public class Hello implements Function<HelloInputArguments, String> {
public String apply(HelloInputArguments input) {
return input.getContent();
}
}
I thin with Tuple are allowed only if you want to pass multiple Fluxs in doc

Mockito: multiple calls to mocked UriBuilder keep appending to original URI

I am currently writing some unit tests for legacy code in a rest api application. One particular api call generates a list of URIs using the UriInfo api from Jax-RS. When I mock this interface, I am able to get the tests to run, but each call to the interface appears to reuse the same URI object and appends to it over and over, rather then a new URI object being passed to the tested code. I have a sample class with test that demonstates the issue:
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
#Stateless
#Path("/")
public class MockitoUrl {
#PersistenceContext(name = "MockitoTestPU")
private EntityManager em;
#Context
private UriInfo uriInfo;
#Path("system")
#GET
#Produces("text")
public List<URI> generateUris() {
List<URI> uris = new LinkedList<>();
for (int i=1;i<5;i++) {
final URI uri = uriInfo.getBaseUriBuilder().path(
MockitoUrl.class, "generateUris").build("test");
System.out.println(uri.toString());
}
return uris;
}
}
The unit test:
import java.net.URI;
import java.util.List;
import javax.persistence.EntityManager;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.MockitoAnnotations;
public class MockitoUrlTest {
#Mock
private EntityManager em;
#Mock
private UriInfo uri;
#InjectMocks
MockitoUrl mockitoUrl;
public MockitoUrlTest() {
}
#Before
public void setUp() {
mockitoUrl = new MockitoUrl();
MockitoAnnotations.initMocks(this);
}
#Test
public void testGenerateUris() throws Exception {
System.out.println("generateUris");
String baseUri = "http://server1/api";
when(uri.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri(baseUri));
List<URI>result = mockitoUrl.generateUris();
for (URI u : result) {
assertTrue(u instanceof URI);
System.out.println(u.toString());
}
}
}
When the test is run, the output can be seen thus:
generateUris
http://server1/api/system
http://server1/api/system/system
http://server1/api/system/system/system
http://server1/api/system/system/system/system
What I would have expected is that it would return the same URI each time. In the real application these URIs would have more detail and each would have different data from a JPA class lookup. It seems that the same object is returned each time and then appended to again.
Is there a way to get Mockito to return a fresh URI each time uriInfo.getBaseUriBuilder() is called, or is this a fundamental misunderstanding I have about how Mockito works?
The problem you face is expected behavior. When setting up the mock you create a single instance and that is always returned.
To get what you want I would suggest using when(..).thenAnswer(i -> UriBuilder.fromUri(baseUri))
By this every call executes the provides lambda and it creates a new instance per invocation.

Integration testing for events in the Jersey container listener

I have an application based on Jersey JAX-RS. I need to refactor the event handler and therefore also write a test for it.
I'm trying to do this with the JerseyTest Framework. I created a configuration to extend ResourceConfig, but when I use the target () call the handler is not called.
I will present the situation using code.
Here is an example Resource class:
package com.my.page;
import org.glassfish.hk2.api.messaging.Topic;
import com.my.core.entity.Link;
import com.my.core.location.LinkHitLocationFactory;
import com.my.core.service.LinkService;
import com.my.core.service.link.LinkFinder;
import com.my.core.service.link.LinkFinderFactory;
import com.my.event.LinkHitEvent;
import com.my.exception.FragmentNotFoundException;
import javax.annotation.security.PermitAll;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
#PermitAll
#Path("/")
public class LinkResource {
#Inject
private LinkService linkService;
#Inject
private Topic<LinkHitEvent> linkHitPublisher;
#Inject
private LinkFinderFactory linkFinderFactory;
#Inject
private LinkHitLocationFactory linkHitLocationFactory;
#GET
#Path("/{fragment:[^ ]{1,32}}")
public Response redirect(
#PathParam("fragment") String fragment,
#HeaderParam("Range") String range,
#HeaderParam("User-Agent") String userAgent,
#Context HttpHeaders headers) throws Exception {
LinkFinder linkFinder = linkFinderFactory.getLinkFinder(fragment);
Link link = linkFinder.getLink(fragment);
if (link.isExpired()) {
throw new FragmentNotFoundException(fragment);
}
linkService.insertHit();
linkHitPublisher.publish(new LinkHitEvent(link));
return handlerFactory.getHandler(link).handleGet(link, range).build();
}
}
Event test:
package com.my.page;
import org.glassfish.hk2.extras.events.internal.TopicDistributionModule;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import pl.comvision.hk2.events.ThreadedEventDistributorService;
import com.my.client.CallbackTargetBuilder;
import com.my.core.entity.Link;
import com.my.core.mapper.LinkMapper;
import com.my.core.service.LinkService;
import com.my.page.resource.LinkResource;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.Status.TEMPORARY_REDIRECT;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
#RunWith(MockitoJUnitRunner.class)
public class CallbackEventTest extends JerseyTest {
#Mock
private LinkMapper linkMapper;
#Mock
private LinkService linkService;
private CallbackTargetBuilder callbackTargetBuilder;
private final String callbackUrl = "";
#Override
protected Application configure() {
this.callbackTargetBuilder = spy(new CallbackTargetBuilder(this.callbackUrl));
ResourceConfig config = new ResourceConfig(LinkResource.class);
config.register(new TopicDistributionModule());
config.register(new AbstractBinder() {
#Override
protected void configure() {
addActiveDescriptor(ThreadedEventDistributorService.class).setRanking(100);
}
});
config.register(new EventsContainerListener(CallbackEventHandler.class));
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(linkMapper).to(LinkMapper.class);
bind(linkService).to(LinkService.class);
bind(mock(LinkService.class)).to(LinkService.class);
bind("").to(String.class).named("varPath");
bind("127.0.0.1").to(String.class).named("requestIP");
bind(callbackTargetBuilder).to(CallbackTargetBuilder.class);
}
});
return config;
}
#Test
public void publish_event() {
Link link = mock(Link.class);
when(link.getUrl()).thenReturn("example");
when(link.getName()).thenReturn("test");
when(linkMapper.getByName(anyString())).thenReturn(link);
Response response = target("/testY").property("jersey.config.client.followRedirects", false).request().get();
assertEquals(TEMPORARY_REDIRECT.getStatusCode(), response.getStatus());
verify(callbackTargetBuilder).build();
}
}
For testing purposes, I only injected callbackTargetBuilder into the handler, and called the build method on it to verify the call:
package com.my.page;
import org.glassfish.hk2.api.messaging.MessageReceiver;
import org.glassfish.hk2.api.messaging.SubscribeTo;
import org.jvnet.hk2.annotations.Service;
import com.my.client.CallbackTargetBuilder;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
#Service
#Singleton
#MessageReceiver
public class CallbackEventHandler {
#Named("callbackUrl")
private String url;
#Inject
private CallbackTargetBuilder callbackTargetBuilder;
#MessageReceiver
public void handle(#SubscribeTo LinkHitEvent event) {
Form form = new Form();
form.param("id", event.getLink().getId().toString());
form.param("name", event.getLink().getName());
callbackTargetBuilder.build();
Client client = ClientBuilder.newClient();
client.target(url).request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
}
}
Edit:
I tried to register dependencies differently, but it does not bring satisfactory results. Each time verification fails:
verify (callbackTargetBuilder) .build ();
Looking for information I found that I can configure the DeploymentContext, but I don't know if this is the right direction.
Edit the second:
A quick test shows that I may have some more basic problem with mocking. Because the call:
verify (linkService) .insertHit (anyObject ());
It also fails.
I will write only for posterity that the above code is correct. The problem was a lot of small bugs in the tested code and how to mock it.

Jersey: ContainerRequestFilter throwing Error

I am using dropwizard with jersey,
I defined a custom provider of ContainerRequestFilter by the following method
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.util.List;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.MultivaluedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Provider
public class CustomRequestFilter implements ContainerRequestFilter{
private final static Logger logger = LoggerFactory.getLogger(CustomRequestFilter.class);
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
MultivaluedMap<String,String> myHeaders = requestContext.getHeaders();
List<String> appId = myHeaders.get("myHeader");
logger.info(myHeader.get(0));
logger.info("Hello Word, it is customRequestFilter");
}
}
but the System is throwing an error
ERROR [2014-05-26 10:04:46,556] com.sun.jersey.server.impl.container.filter.FilterFactory: The filter, of type myPackage.CustomRequestFilter, MUST be of the type String, String[], Class<? extends
com.sun.jersey.spi.container.ContainerRequestFilter>, or an instance of
com.sun.jersey.spi.container.ContainerRequestFilter. The filter is ignored.
I think this is just a question of using the right imports. Try this:
import java.util.List;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
#Provider
public class CustomRequestFilter implements ContainerRequestFilter {
private final static Logger logger = LoggerFactory.getLogger(CustomRequestFilter.class);
public ContainerRequest filter(ContainerRequest requestContext) {
MultivaluedMap<String,String> myHeaders = requestContext.getRequestHeaders();
List<String> appId = myHeaders.get("myHeader");
logger.info(appId.get(0));
logger.info("Hello Word, it is customRequestFilter");
return requestContext;
}
}

Spring MockMvc redirectedUrl with pattern

I have a simple PersonController class that provides save() method to persist the object from http post request.
package org.rw.controller;
import java.sql.Timestamp;
import java.util.List;
import org.rw.entity.Person;
import org.rw.service.PersonService;
import org.rw.spring.propertyeditor.TimestampPropertyEditor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping(value="/person")
public class PersonController {
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
#Autowired
private PersonService personService;
#Autowired
TimestampPropertyEditor timestampPropertyEditor;
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Timestamp.class, "dob", timestampPropertyEditor);
}
#RequestMapping(value="/save", method=RequestMethod.POST)
public String save(Model model, Person person) {
Long personId = personService.save(person);
return "redirect:view/" + personId;
}
}
As the save() method returns as return "redirect:view/" + personId;. It will be diffrerent for every request. it may be like "view/5" or "view/6" depending on the id of the object that has been persisted.
Then i have a simple class to test the above controller with spring mocking.
package org.rw.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.rw.service.UserService;
import org.rw.test.SpringControllerTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
public class PersonControllerTest extends SpringControllerTest {
#Autowired
private UserService userService;
#Test
public void add() throws Exception {
mockMvc.perform(get("/person/add", new Object[0])).andExpect(status().isOk());
}
#Test
public void save() throws Exception {
UserDetails userDetails = userService.findByUsername("anil");
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrl("view")
);
}
}
now here i have a problem that redirectedUrl("view") is rejecting value "view/5". I have tried redirectedUrl("view*") and redirectedUrl("view/*") but its not working.
Edit :
Here I have got a workaround as per below
MvcResult result = mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "MALE")
.param("dob", "11/02/1989")
).andExpect(
//redirectedUrl("view")
status().isMovedTemporarily()
).andReturn();
MockHttpServletResponse response = result.getResponse();
String location = response.getHeader("Location");
Pattern pattern = Pattern.compile("\\Aview/[0-9]+\\z");
assertTrue(pattern.matcher(location).find());
but still i am looking for the proper way.
update:
I have posted the same issue on spring jira here :
Since spring 4.0 you can use redirectedUrlPattern as pointed by Paulius Matulionis
As of spring 3.x this is not supported out of the box but you can easily add you custom result matcher
private static ResultMatcher redirectedUrlPattern(final String expectedUrlPattern) {
return new ResultMatcher() {
public void match(MvcResult result) {
Pattern pattern = Pattern.compile("\\A" + expectedUrlPattern + "\\z");
assertTrue(pattern.matcher(result.getResponse().getRedirectedUrl()).find());
}
};
}
And use it like build-in matcher
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrlPattern("view/[0-9]+")
);
Since 4.0 it is available in Spring itself.
Please check here.

Categories

Resources