I have jersey web services that uses spring for dependency injection (spring-jersey module) and hibernate for object-relational mapping (ORM). In order to develop integration tests taking into account the following conditions:
According to this https://java.net/jira/browse/JERSEY-2412 Jersey project JIRA task HttpServletRequest is null and that's as shown in the resolution of the task Works as desgined. When running integration tests on Grizzly container, it runs the integration tests on http server hence, any dependence on servlet-based features such as HttpServletRequest, HttpServletResponse, etc. is not available.
There seems to be no standard solution on how to address this issue, and Jersey community is obviously open to such features being developed by contributors as stated in this https://java.net/jira/browse/JERSEY-2417 JIRA ticket. Until this feature is implemented, what are the possible workaround solutions? Based on my research I come cross a few posts that state:
So, what's the best solution to successfully run integration tests on jersey-based web servers that uses spring-jersey bridge for dependency injection and rely on Servlet-based features?
This is standard behaviour from Jersey and the to allow servlet-based features such as HttpServletRequest is not available yet. Based on my research, I could achieve the following conditions
By starting/stopping A grizzly container manually and deploying the grizzly container instance on a custom jersey-based WebappContext. The steps are as follow in case anyone else come across such issue
Step #1
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ResourceEndpointIntegrationTest{
@Context
private HttpServletRequest httpReq;
private static Logger logger = Logger.getLogger(ResourceEndpointIntegrationTest.class);
public static final String BASE_URI = "http://localhost:8989/";
private static HttpServer server = null;
@BeforeClass
public static void initTest() {
RestAssured.baseURI = "http://localhost:8989/";
}
...
}
Use SpringJUnit4ClassRunner.class and @ContextConfiguration to load the applicationContext.xml for tests. Also declare @Context HttpServletRequest and create an instance of HttpServer to be used later. I use @BeforeClass here for Rest-Assured specific purposes (you don't have to use it), its optional.
Step #2
@Before
public void setup() throws Exception {
if (server == null) {
System.out.println("Initializing an instance of Grizzly Container ...");
final ResourceConfig rc = new ResourceConfig(ResourceEndpointIntegrationTest.class, ..., ..., ...); //update
WebappContext ctx = new WebappContext("IntegrationTestContext");
//register your listeners from web.xml in here
ctx.addListener("com.xxx.yyy.XEndpointServletContextListener");
//register your applicationContext.xml here
ctx.addContextInitParameter("contextConfigLocation", "classpath:applicationContext.xml");
//ServletRegistration is needed to load the ResourceConfig rc inside ServletContainer or you will have no
//Servlet-based features available
ServletRegistration registration = ctx.addServlet("ServletContainer",
new ServletContainer(rc));
//Initialize the Grizzly server passing it base URL
server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI));
//Deploy the server using our custom context
ctx.deploy(server);
}
}
The setup in @Before is run for every test but, the if condition will force the setup method to act like @BeforeClass allowing us to initialize the server once for the entire test class minus the static nature of @BeforeClass.
The reason I initialize the server once for all tests inside a test class is because, if we don't then we will have the following workflow
Above is very time consuming and technically not feasible, hence initialize container once (which is why I don't extend JerseyTest because I want to control the startup/shutdown of the test container).
#Step 3
@AfterClass
public static void tearDown() throws Exception {
System.out.println("Integration tests completed. Tear down the server ...");
if (server != null && server.isStarted()) {
server.shutdownNow();
System.out.println("Grizzly instance shutdown completed");
}
}
In step 3 we use @AfterClass to shutdown the grizzly instance used for integration testing purposes.
And a sample test looks as follow
@Test
public void testCreateSomethingReturnSuccessfully() {
JSONObject something = new JSONObject();
cust.put("name", "integrationTest");
cust.put("age", 33);
given().
contentType(ContentType.JSON).
body(something.toString()).post("/someEndpoint").
then().
statusCode(200).
assertThat().
body("id", greaterThan(0)).
body("name", equalTo("integrationTest")).
body("age", equalTo(33));
}
(Gradle) Some of the relevent depdendencies
compile group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet', version: '2.23.2'
compile group: 'org.glassfish.jersey.test-framework.providers', name:'jersey-test-framework-provider-grizzly2', version:'2.23.2'
compile group: 'org.springframework', name:'spring-test', version:'4.3.2.RELEASE'
compile group: 'io.rest-assured', name:'rest-assured', version:'3.0.1'
// Spring
compile group: 'org.springframework', name: 'spring-core', version: '4.3.2.RELEASE'
compile group: 'org.springframework', name: 'spring-beans', version: '4.3.2.RELEASE'
compile group: 'org.springframework', name: 'spring-web', version: '4.3.2.RELEASE'
compile group: 'org.springframework', name: 'spring-jdbc', version: '4.3.2.RELEASE'
compile group: 'org.springframework', name: 'spring-orm', version: '4.3.2.RELEASE'
// Jersey-Spring bridge
compile (group: 'org.glassfish.jersey.ext', name: 'jersey-spring3', version: '2.23.2'){
exclude group: 'org.springframework', module: 'spring-core'
exclude group: 'org.springframework', module: 'spring-web'
exclude group: 'org.springframework', module: 'spring-beans'
exclude group: 'org.springframework', module: 'spring-jdbc'
exclude group: 'org.springframework', module: 'spring-orm'
}
Some of the imports
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;