How to test Rest API with Arquillian and Mock - java

I would like to test following class implements JAX-RS with CDI Injects inside.
#Path("area")
public class AreaService {
#Inject
#SuppressWarnings("unused")
private Logger logger;
#Context
private SecurityContext context;
#Inject
private AreaGateway areaGateway;
#GET
#Path("{area-name}/team-project/{tp-id}")
#Produces(MediaType.APPLICATION_JSON)
public Response getAreaByTeamProjectAndName(#PathParam("tp-id") long tpId, #PathParam("area-name") String areaName)
throws WebApplicationException {
Area area = areaGateway.getAreaByTeamProjectAndName(tpId, areaName);
return Response.status(Response.Status.OK).entity(area).build();
}
#POST
#Path("team-project/{tp-id}")
#Auditable
#Consumes(MediaType.APPLICATION_JSON)
public Response createArea(#PathParam("tp-id") long tpId, #Payload Area area) throws WebApplicationException {
long areaId = areaGateway.createArea(tpId, area, context);
return Response.status(Response.Status.CREATED).entity(areaId).build();
}
}
I have choosen Arquillian as test framework and I have used the extension for the REST APIs.
In particular i have tried to follow the Warp REST example, but i can't find a clean and clear sample to test my REST API with mocking inject dependency like AreaGateway with any mocking framework like Mockito.
I would like to do something like this:
#RunWith(Arquillian.class)
public class RestClientTestCase {
#ArquillianResource
public URL contextPath;
private AreaService areaService;
private AreaGateway areaGateway;
#Deployment(testable = false)
public static WebArchive create() {
WebArchive war = ShrinkWrap.create(WebArchive.class)
.addClasses(SLF4JProducer.class, AreaGateway.class, JAXRSActivator.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "WEB-INF/beans.xml");
MavenResolverSystem resolver = Maven.resolver();
war.addAsLibraries(resolver.loadPomFromFile("pom.xml").resolve("org.mockito:mockito-all:1.10.8").withTransitivity().asSingleFile());
return war;
}
#BeforeClass
public static void setUpClass() {
// initializes the rest easy client framework
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
}
#Before
public void setUp() {
areaGateway = Mockito.mock(AreaGateway.class);//real bean replaced by mocked one
Mockito.when(areaGateway.getAreaByTeamProjectAndName(0, "areaName")).thenReturn(new Area());
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("" + "rest");
areaService = target.proxy(AreaService.class);
}
#Test
#RunAsClient
public void testStockGetWarp() {
Response response = areaService.getAreaByTeamProject(0);
assertEquals("The service returned status code.", 201, response.getStatus());
response.close();
}
}
Could someone help me to develop minimal test sample with exact Maven dependencies? Is it possible to mock CDI dependency with some framework according to Arquillian?

Related

Spring Boot #Async annotation and MockRestServiceServer

I'm using Spring Boot 2.0.6 and Java 10. I did the following service that only hits an external rest api using RestTemplate.
#Service
#Slf4j
public class DbApiClientImpl implements DbApiClient {
private final String URL_DELIMITER = "/";
private RestTemplate restTemplate;
private String url;
public DbApiClientImpl(
RestTemplateBuilder restTemplate,
#Value("${dbapi.namespace}") String namespace,
#Value("${dbapi.url}") String uri,
#Value("${dbapi.username}") String username,
#Value("${dbapi.password}") String password) {
this.restTemplate = restTemplate.basicAuthorization(username,
password).build();
this.url = namespace.concat(uri);
}
#Override
#Async("asyncExecutor")
public Merchant fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return response.getBody();
}
}
And the following test using MockeRestServiceServer:
#RunWith(SpringRunner.class)
#RestClientTest(value = {DbApiClient.class})
public class DbApiClientTest {
private static final String TEST_NAME = "test";
private static final String TEST_NAME_BAD_REQUEST = "test-
1";
private static final String TEST_NAME_SERVER_ERROR =
"test-2";
#Autowired DbApiClient dbApiClient;
#Value("${dbapi.namespace}")
private String namespace;
#Value("${dbapi.url}")
private String dbApiUrl;
#Autowired private MockRestServiceServer mockServer;
#Autowired private ObjectMapper objectMapper;
#Test
public void test() throws
JsonProcessingException, IOException {
Merchant mockMerchantSpec = populateFakeMerchant();
String jsonResponse =
objectMapper.writeValueAsString(mockMerchantSpec);
mockServer
.expect(manyTimes(),
requestTo(dbApiUrl.concat("/").concat(TEST_NAME)))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(jsonResponse,
MediaType.APPLICATION_JSON));
assertNotNull(dbApiClient.fetchMerchant(TEST_NAME));
}
The thing is that I'm getting the following exception when I run the test "No further request expected HTTP GET http://localthost... excecuted"
So seems that the #Async is borking MockerServerService response...
Also, If I commented the #Async annotation everything works just fine and I get all test green.
Thanks in advance for your comments.
Update:
As per #M.Deinum's comment. I removed the CompletableFuture from the service but I'm still getting the same exception.
The problem is your code and not your test.
If you read the documentation (the JavaDoc) of AsyncExecutionInterceptor you will see the mention that only void or Future is supported as a return type. You are returning a plain object and that is internally treated as void.
A call to that method will always respond with null. As your test is running very quickly everything has been teared down already (or is in the process of being teared down) no more calls are expected to be made.
To fix, fix your method signature and return a Future<Merchant> so that you can block and wait for the result.
#Override
#Async("asyncExecutor")
public Future<Merchant> fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return CompletableFuture.completedFuture(response.getBody());
}
Now your calling code knows about the returned Future as well as the Spring Async code. Now in your test you can now call get on the returned value (maybe with a timeout to receive an error if something fails). TO inspect the result.

Creating and injecting a per request scoped variable

I would like to have a variable that follows along the full lifecycle of a request in java EE.
For example it could be for a logging function, so that I can filter all log entries by request.
The key part that I want to get at is that it must be relatively easy to implement in an already existing application so if possible some sort of dependency injection that gets the variable related to the specific request.
I've tried injectiong a #RequestScoped variable, but it doesn't work since it is only scoped to the container. I would need to be able to inject the same object to different containers. Is this at all possible?
EDIT: I want something along the lines of this:
#RequestScoped
public class RequestVariables {
public String id;
}
#Stateless
public class Logger {
#Inject
private RequestVariables requestVariables;
public void log(String message) {
System.out.println(requestVariables.id + ":" + message);
}
}
#Stateless
public class Service {
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
public void save(String data) {
logger.log("Save");
session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
}
}
public class API {
#Inject
private Service service;
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
#Path("/1")
#GET
public Response get(#QueryParam("data") String data) {
requestVariables.id = UUID.randomUUID().toString()
service.save(data);
logger.log("Get");
return Response.status(204).build();
}
}
Currently this is what I have experimented with:
#RequestScoped
public class RequestScope {
private int test = 0;
public RequestScope(int test) {
this.test = test;
}
public RequestScope(){}
public int getTest() {
return test;
}
public void setTest(int test) {
this.test = test;
}
}
#Provider
public class RequestScopeFilter implements ContainerRequestFilter {
#Inject
private javax.inject.Provider<RequestScope> requestScopeProvider;
#Context
private HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestScopeProvider.get().setTest(42);
request.setAttribute("test", "superTest");
}
}
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {
#Context
private HttpServletRequest httpServletRequest;
#Inject
private Provider<RequestScope> requestScopeProvider;
public void test() {
RequestScope scope = requestScopeProvider.get();
String test = (String)httpServletRequest.getAttribute("test");
}
}
So when I get the scope from my service then it is a new object with test set to 0, and then it throws an NPE since httpServletRequest is null
option #1
Implement an Interceptor and set the request id as HttpServletRequest attribute:
#AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
HttpServletRequest request = [..] // getHttpServletRequest(ic);
request.setAttribute("request-id", UUID.randomUUID().toString());
return ic.proceed();
}
Then use HttpServletRequest everywhere you need it
#Context
private HttpServletRequest httpRequest;
option #2
If want just to filter your logs by an unique id, you can configure your Logger to print the thread name: [%t]
Example: Log4j PatternLayout
option #3
Use a custom java bean to encapsulate the request data (query param, request id etc.) and pass this bean across your application services.
public class API {
#Inject
private Service service;
#Path("/1")
#GET
public Response get(MyCustomRequestBean data) {
service.doSomejob(data);
return Response.status(204).build();
}
}
Set the request id and query param in ParamConverter:
Jax-RS ParamConverter - ParamConverterProvider method return type mismatch
You can inject a provider in your service:
#Inject
Provider<RequestVariables> vars
And then call get () to get the instance. If you try to get () in a thread outside a request scope context you'll get an exception. I would however try to structure in a way that would not allow this to happen
A solution that I found is to use ThreadLocal variables. It seems rather dirty, but it works since each request is executed on it's own thread(as far as I am aware). So this is what I got:
public class RequestScope {
private static final ThreadLocal<String> id = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public static String get() {
return id.get();
}
}
With that I can also easily exchange the ThreadLocal to return something more specific if so desired.
And I can get the variables from pretty much anywhere, assuming that the request is not starting a different thread

Mock Service inside resource using jersey test framwork

I have a resource for rest API which uses a service.
This service has a constructor with parameters.
I want to test this resource and to mock this service.
This Question: How to pass parameters to REST resource using Jersey 2.5
wasn't helpful because they used #Inject and I cannot use it.
Any suggestions?
The second question is how do I pass parameter to test this resouce:
My code is:
#Path("/2/{subversion: [0-3]}/users")
public class UserResource {
Logger log = Logger.getLogger(UserResource.class);
private MyService service;
public void setService(Service ser) {
this.service = ser;
}
#Context HttpServletRequest currentRequest;
#GET
#Produces("application/json")
public Response getUsers(#Context HttpHeaders httpHeaders, #Context UriInfo
uriInfo) {
// my function
}
}
How can I pass "httpHeaders" and "UriInfo".
My test looks like this:
Response response = target("/2/0/users/").request().get();
Users users = response.readEntity(Users.class);
assertNotNull(users);
For the service, it's good practice to either inject through the constructor or setter. This makes it easy to mock and pass in during unit testing. As for the mocking, you should use a framework like Mockito. Then you can do stuff like
MyService service = Mockito.mock(MyService.class);
when(service.getObject()).thenReturn(new Object());
HttpHeaders headers = Mockito.mock(HttpHeaders.class);
when(headers.getHeaderString("X-Header")).thenReturn("blah");
UriInfo uriInfo = Mockito.mock(UriInfo.class);
when(uriInfo.getRequestUri()).thenReturn(URI.create("http://localhost"));
Then you can just pass all these mocks to your resource class when UNIT testing.
For INTEGRATION testing you won't need to mock the headers or uriinfo. The actual ones will get passed in. But you can still mock the service if you want. Here's an example
public class MockServiceTest extends JerseyTest {
public static interface Service {
String getMessage(String name);
}
#Path("message")
public static class MessageResource {
private final Service service;
public MessageResource(Service service) {
this.service = service;
}
#GET
public String get(#QueryParam("name") String name,
#Context HttpHeaders headers,
#Context UriInfo uriInfo) {
String nameQuery = uriInfo.getQueryParameters().getFirst("name");
String header = headers.getHeaderString("X-Header");
assertNotNull(nameQuery);
assertNotNull(header);
return service.getMessage(name);
}
}
private Service service;
#Override
public ResourceConfig configure() {
service = Mockito.mock(Service.class);
return new ResourceConfig().register(new MessageResource(service));
}
#Test
public void testIt() {
Mockito.when(service.getMessage("peeskillet")).thenReturn("Hello peeskillet");
Response response = target("message").queryParam("name", "peeskillet").request()
.header("X-Header", "blah")
.get();
assertEquals(200, response.getStatus());
assertEquals("Hello peeskillet", response.readEntity(String.class));
}
}

How do I execute a method in Spring when the Server is ready?

I have the following problem: I'm using Spring-Boot for a little private web-based project and I want Spring to make a call to a webservice when it's started. And by started I mean "when my application is ready to handle requests".
I've already tried implementing the ApplicationListener<ContextRefreshedEvent> but it did not work, as the Event happend to early (i.e. before the embedded server was ready to handle request). Also the options mentioned in this question did not solve this problem.
My question now is: Is there any possibilty to tell Spring to execute something after the server has finished starting up and is ready to handle requests?
EDIT (in response to Daniel's answer):
The problem is that I need some injected properties to make that webservice call, and since injecting static values does not work in spring this approach is no option.
My listener, that does what I want, just a bit too early looks something like this:
#Component
public class StartupListener implements ApplicationListener{
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#Override
public final void onApplicationEvent(ContextRefreshedEvent event) {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
SECOND EDIT:
Although this question solves a very similar problem it seems like I'm not able to inject into the object because it needs to have a constructor of the form (org.springframework.boot.SpringApplication, [Ljava.lang.String;).
Also it would be desirebale to solve it without having to create the spring.factories file but by using annotations.
If I understand what your problem is, you could call the webservice on your application main, right after it initiates.
public static void main(String[] args) {
new SpringApplication(Application.class).run(args);
//call the webservice for you to handle...
}
I'm not sure if this is what you want...
In your component you can use the #PostConstruct annotation. e.g.
#Component
public class StartupListener {
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#PostConstruct
public final void init() {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
This will fire once the bean has been initialised and autowiring has taken place.
#Component
public class StartUp implements ApplicationListener<WebServerInitializedEvent> {
private WebClient webClient;
#Override
public void onApplicationEvent(WebServerInitializedEvent event) {
String baseUrl = "http://url.com"
webClient = WebClient.create(baseUrl);
executeRestCall(baseUrl+"/info");
}
void executeRestCall(String uri) {
try {
webClient.get()
.uri(uri)
.exchange()
.block();
} catch (Exception e) {
log.error("Request failed for url - {}",uri, e);
}
}}

How to mock a SecurityContext

Endpoints with Jersey.
I want to secure an endpoint with a ContainerRequestFilter
#Provider
#Secured
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
//TODO: on logger here...
System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod());
requestContext.getHeaders().add("X-Secured-By", "Jersey >_<");
System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme());
if (securityContext == null || !securityContext.isUserInRole("privileged")) {
requestContext.abortWith(new UnauthorizedResponse().getResponse());
}
}
}
The annotation #Secured:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {}
So I can do this:
#Path("foobar")
public class FooResource {
//...
#Context
SecurityContext securityContext;
//...
#GET
#Secured
#Path(value = "foo")
#Produces(MediaType.APPLICATION_JSON)
public Response getFoo(#Context SecurityContext sc, #Context UriInfo ui, #Context HttpHeaders hh) {
// ...
}
//...
And I'm doing it right (I think), because with my test I don't even pass through the getFoo endpoint but is the ContainerRequestFilter that kicks me out. In fact I receive this (the "X-Secured-By" header is hand-made):
Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]}
Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}}
Now it would be nice to mock the SecurityContext.
This is what I'm doing... and if I'm here, it's obviously silly and/or wrong.
public class UrlerResourceTest extends JerseyTest {
//....
#Override
public TestContainerFactory getTestContainerFactory() {
GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory();
System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory);
// just for debugging...
return grizzlyTestContainerFactory;
}
#Test
public void testSecuredEndpoint() throws JSONException {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true);
Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking...");
ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class);
final Response response = target("foobar")
.path("foo")
.request(MediaType.APPLICATION_JSON)
.get();
System.out.println(getFormattedStringResponseInfo(response));
JSONObject entity = new JSONObject(response.readEntity(String.class));
assertTrue(entity.get("secured").equals(true));
assertTrue(response.getHeaders().containsKey("X-Secured-By"));
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
How can I mock the SecurityContext in my tests?
Thank you so much in advance.
Disclaimer: I'm not really a Mockito user, but from what I understand, mocking is used for situations where you have injected class dependencies (fields), and you mock those dependencies. In which case you still need to set the field with the mocked object. For example
public class TestClass {
TestService testService;
public void doTest() {
System.out.println(testService.getString());
}
public void setTestService(TestService testService) {
this.testService = testService;
}
}
public class TestService {
public String getString() {
return "Hello world";
}
}
#Test
public void toTest() {
TestService testService = Mockito.mock(TestService.class);
Mockito.when(testService.getString()).thenReturn("Hello Squirrel");
TestClass testClass = new TestClass();
testClass.setTestService(testService);
testClass.doTest();
}
You can see we are setting the the TestService in the TestClass with the mocked object. It's not greatest example, as we could simple instantiate TestService, but it shows, from my understanding, how the mocking should work.
That being said, I don't see how it is possible to do this with the AuthorizationRequestFilter, as it's handled by the test container, and we are not instantiating it for a unit test. Even if we were, it would seem intrusive (and redundant) to add a SecurityContext field.
So without a full integration test, where we are starting the server, and using the server's authentication capabilities, it will be difficult to handle the SecurityContext per this use case, as the SecurityContext is created by the container, taking information from the underlying servlet containers authentication mechanism.
One way you can achieve this though (which IMO doesn't seem very elegant - but works), without a full integration test, is to create a a filter which performs before your AuthorizationRequestFilter, and set the SecurityContext from there. Testing aside, this is actually pretty common in cases where we need to implement outr own custom authentication mechanism.
An example of how you might do this for your unit test, might be something like:
public class UrlerResourceTest extends JerseyTest {
...
#Override
public Application configure() {
return new ResourceConfig(FooResource.class)
.register(AuthorizationRequestFilter.class)
.register(AuthenticationFilter.class);
}
#Provider
#Priority(Priorities.AUTHENTICATION)
public static class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "Stackoverflow";
}
};
}
#Override
public boolean isUserInRole(String string) {
return "privileged".equals(string);
}
#Override
public boolean isSecure() { return true; }
#Override
public String getAuthenticationScheme() { return "BASIC"; }
});
}
}
...
}
This filter will perform before the AuthorizationRequestFilter because of the #Priority annotation. We've set it to Priorities.AUTHENTICATION which will before before any other filter without such annotation. (See Priorities API and Priorities with Jersey. Also the SecurityContext will be passed along between filters and also be injected into your resource class.
As I said, I don't think this is very elegant to have to create another filter, but it works for this purpose. Also I am not too familiar with the Jersey Test Framework, as I'm still beginning with it, but there are many configuration options for deployment within a servlet context. I don't know if we can configure the needed authentication mechanism for this case, but it might be something worth looking into.
Edit: In the beginning I explained about setting the field for the test object, but we can also pass the mocked object to a method. For example we could mock the ContainterRequestContext in the filter method, and call filter ourselves, passing the mocked ContainerRequestContext. But this is only useful when we are actually unit testing the filter class and instantiating it ourselves, which is not the case here.

Categories