Best way to unit test JAX-RS Controller? - java

I am learning the library javax.ws.rs so I wrote a toy API.
For instance, I've got this POST method working. I've seen that there are many libraries for testing, like Jersey or Rest Assured, but, how can I unit test that response code is 200 and that the content is "hello"?
#POST
#Path("/getFoo")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
Foo getFoo(#BeanParam final Foo foo)
{
return new Foo("hello");
}

For Jersey web services testing there are several testing frameworks, namely: Jersey Test Framework (https://jersey.java.net/documentation/1.17/test-framework.html) and REST-Assured (https://code.google.com/p/rest-assured) - see here a comparison/setup of both (http://www.hascode.com/2011/09/rest-assured-vs-jersey-test-framework-testing-your-restful-web-services/).
package com.example.rest;
import static com.jayway.restassured.RestAssured.expect;
import groovyx.net.http.ContentType;
import org.junit.Before;
import org.junit.Test;
import com.jayway.restassured.RestAssured;
public class Products{
#Before
public void setUp(){
RestAssured.basePath = "http://localhost:8080";
}
#Test
public void testGetProducts(){
expect().statusCode(200).contentType(ContentType.JSON).when()
.get("/getProducts/companyid/companyname/12345088723");
}
}

Related

Exposing endpoint URL in tests

Let's go straight to the problem:
I want to test a method that wants URL of Rest endpoint as a parameter. That method is using RestTemplate internally to send a request to that URL, so it needs to be full URL, for example http://localhost:8080/rest. Because of that I also have no way to mock RestTemplate.
I wanted to create simple #RestController but it seems that Spring is not creating endpoint when running tests.
I've tried creating MockMvc but it's not what I want. I have no way of getting the IP and port of MockMvc's created endpoint because no actual endpoint is created.
So, what I want is to make my #RestController accessible in tests by sending requests to URL, for example: http://localhost:8080/rest.
It's my first time creating a test like that, I will be grateful for your help. I was searching for an answer but I couldn't find a solution for my problem.
Edit:
Here's some code:
Unfortunately I can't post all the code but what I'm posting should be enough:
I have my endpoint like(names changed):
#RestController
public class EndpointController {
#RequestMapping(value = "/rest", method = RequestMethod.POST)
public List<Output> doSomething(#RequestBody Input[] requestList){
...
}
}
It's endpoint only for testing, it mimics the real endpoint. Then during my test I'm creating an object like:
new EndpointClient("http://localhost:8080/rest")
which has inside something like this:
ResponseEntity<Output[]> responseEntity = restTemplate.exchange(endpointURL,HttpMethod.POST, httpEntity, Output[].class);
Method having restTemplate request isn't called directly during testing(it's called by another method).
So I need to pass that URL to the Client object.
If you want to test your web application, checkout the Getting Started: Testing the Web Layer documentation.
Spring Boot is providing some useful annotations like #SpringBootTest, #AutoConfigureMockMvc, ...
See also the TestRestTemplate, which can be autowired to your WebMvcTest.
edit:
An example, copied from the documentation mentioned above:
package hello;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello World");
}
}
I found a solution for my problem.
I did it with WireMock: http://wiremock.org/
First I had to create Transformer for my request, so the response depends on request, something like:
public class MyTransformer extends ResponseTransformer {
#Override
public Response transform(final Request request,
final Response response,
final FileSource fileSource,
final Parameters parameters){
HttpHeaders headers = new HttpHeaders(new HttpHeader("Content-Type", "application/json"));
if(request.getUrl().contains("/rest")){
...
return Response.Builder.like(response).but().body(...).headers(headers).build();
} else
return Response.Builder.like(response).but().status(404).headers(headers).build();
}
#Override
public String getName() {
return "swapper";
}
}
In my test I needed to put
WireMockServer server = new WireMockServer(wireMockConfig().port(3665).extensions("package.name.endpoint.MyTransformer"));
server.stubFor(post("/rest"));
It created exactly what I wanted. Code above is extremely simple, probably not that good and needs work but it shows the basics.

Add a header to RestTemplate using #Before advice

I'm having trouble adding a custom Header to a RestTemplate using AOP in Spring.
What I have in mind is having some advice that will automatically modify execution of RestTemplate.execute(..) by adding this one Header. Other concern is targeting a particular RestTemplate instance that is a member of a Service which requires having this Header passed.
Here is the code of my Advice as it looks like now:
package com.my.app.web.controller.helper;
import com.my.app.web.HeaderContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
#Aspect
public class MyAppHeaderAspect {
private static final String HEADER_NAME = "X-Header-Name";
#Autowired
HeaderContext headerContext;
#Before("withinServiceApiPointcut() && executionOfRestTemplateExecuteMethodPointcut()")
public void addHeader(RequestCallback requestCallback){
if(requestCallback != null){
String header = headerContext.getHeader();
}
}
#Pointcut("within (com.my.app.service.NeedsHeaderService)")
public void withinServiceApiPointcut() {}
#Pointcut("execution (* org.springframework.web.client.RestTemplate.execute(..)) && args(requestCallback)")
public void executionOfRestTemplateExecuteMethodPointcut(RequestCallback requestCallback) {}
}
The problem that I'm having is how to modify RequestCallback to add my header. Being an interface it looks rather empty, on the other hand I'd rather not use a concrete implemntation because then I'd have to manually check if the implementation matches expected class. I'm beginning to wonder if this is really a correct method to do this. I found this answer Add my custom http header to Spring RestTemplate request / extend RestTemplate
But it uses RestTemplate.exchange() when I've checked that on my execution path RestTemplate.execute() is being used. Anyone has any ideas here?

Mockito - test if return type is same as expected

I am beyond frustrated because I can't figure this out. I've been over quite a lot of articles but i can't figure out how to solve this problem, and yet i think i overlook something very simple.
I have a class with a couple of endpoints, one of them is:
#GET
#Path("courses")
#Produces(MediaType.APPLICATION_JSON)
public Response courses(#QueryParam("token") String token) {
Integer studentId = iStudentDAO.getStudentIdByToken(token);
if(studentId == null){
return Response.status(403).build();
} else {
GetStudentsResponse studentCourses = iStudentDAO.getStudentCourses(studentId);
return Response.status(200).entity(courses.name).build();
}
}
This method should always return a Response.status. And thats exactly what i want to test. I know its not possible to unit tests the actual response (200 or 403) but i would like to know how to test if at least Response.status is returned. Im using Mockito for this in my Maven project.
#Test
public void testGetAllCourses () {
String token = "100-200-300";
StudentRequests studentRequests = mock(StudentRequests.class);
when(studentRequests.courses(token)).thenReturn(Response.class);
Response expected = Response.class;
assertEquals(expected, studentRequests.courses());
}
Could anyone explain me how to accomplish this? :)
You should be able to test your specific responses.
Assuming, the method courses is in a class Controller. And this is your class under test, your target should be to test the code here, which is the code that you have written in your controller.
Here is an example:
package test;
import static org.junit.Assert.*;
import org.apache.catalina.connector.Response;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class ControllerTest {
#Mock
IStudentDAO iStudentDAO;
#InjectMocks
Controller controller;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCoursesWhenStudentIDNotFound() {
Mockito.when(iStudentDAO.getStudentIdByToken("1234")).thenReturn(null);
Response response = controller.courses("1234");
assertEquals(403, response.getStatus())
}
}
Similarly, in the next test case, you can mock the IStudentDAO, to return a studentId and then courses for this studentId and validate that you do get them in the response.

Unit testing jersey Restful Services

I'm new to unit testing and I want to test some jersey services in a project. We are using Junit. Please guide me to write test cases in better way.
CODE:
#GET
#Path("/getProducts/{companyID}/{companyName}/{date}")
#Produces(MediaType.APPLICATION_JSON)
public Object getProducts(#PathParam("companyID") final int companyID,
#PathParam("date") final String date, #PathParam("companyName") final String companyName)
throws IOException {
return productService.getProducts(companyID, companyName, date);
}
Above mentioned service is working fine and I want to write junit test case to test above mentioned method. Above method will retrieve list of products (List<Product>) in JSON format. I would like to write test case to check response status and json format.
NOTE: We are using Jersey 1.17.1 version.
Help would be appreciated :)
For Jersey web services testing there are several testing frameworks, namely: Jersey Test Framework (already mentioned in other answer - see here documentation for version 1.17 here: https://jersey.java.net/documentation/1.17/test-framework.html) and REST-Assured (https://code.google.com/p/rest-assured) - see here a comparison/setup of both (http://www.hascode.com/2011/09/rest-assured-vs-jersey-test-framework-testing-your-restful-web-services/).
I find the REST-Assured more interesting and powerful, but Jersey Test Framework is very easy to use too. In REST-Assured to write a test case "to check response status and json format" you could write the following test (very much like you do in jUnit):
package com.example.rest;
import static com.jayway.restassured.RestAssured.expect;
import groovyx.net.http.ContentType;
import org.junit.Before;
import org.junit.Test;
import com.jayway.restassured.RestAssured;
public class Products{
#Before
public void setUp(){
RestAssured.basePath = "http://localhost:8080";
}
#Test
public void testGetProducts(){
expect().statusCode(200).contentType(ContentType.JSON).when()
.get("/getProducts/companyid/companyname/12345088723");
}
}
This should do the trick for you... you can verify JSON specific elements also very easily and many other details. For instructions on more features you can check the very good guide from REST-Assured (https://code.google.com/p/rest-assured/wiki/Usage). Another good tutorial is this one http://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework/.
HTH.
Just ignore the annotations and write a normal unit test that passes the required parameters. The return I thought would usually be of type "javax.ws.rs.core.Response" ... There is a getEntity() method on that can be used. Using a Mock object framework like Mockito could be helpful in this case too.
Are you familiar with Chapter 26. Jersey Test Framework?
public class SimpleTest extends JerseyTest {
#Path("hello")
public static class HelloResource {
#GET
public String getHello() {
return "Hello World!";
}
}
#Override
protected Application configure() {
return new ResourceConfig(HelloResource.class);
}
#Test
public void test() {
final String hello = target("hello").request().get(String.class);
assertEquals("Hello World!", hello);
}
}

PowerMock: how to do EasyMock's expectPrivate() for Mockito?

PowerMock provides the method expectPrivate to mock out private methods, however it appears only in EasyMock api and not the Mockito API.
So, is there an equivalent for PowerMockito? I'm guessing not because I haven't found it and because of this wiki entry. but that doesn't actually prevent PowerMockito from working around it. So, I'm asking this mostly for confirmation and since I think this will be of value for others.
PowerMockito provides ways to mock private methods as well, from the API:
<T> WithOrWithoutExpectedArguments<T> when(Object instance, Method method)
Expect calls to private methods.
verifyPrivate(Object object, org.mockito.verification.VerificationMode verificationMode)
Verify a private method invocation with a given verification mode.
There are a bunch of other signatures of the type described above.
An example:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.eq;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Foo.class)
public class SimpleTest {
#Test
public void testHello() throws Exception {
Foo foo = PowerMockito.spy(new Foo());
foo.sayHello();
PowerMockito.verifyPrivate(foo).invoke("hello", eq("User"));
}
}
class Foo {
public void sayHello() {
System.out.println(hello("User"));
}
private String hello(String user) {
return "Hello " + user;
}
}

Categories

Resources