问题
I am working on fixing the integration tests in one of the projects. Currently, all the integration test classes extend the JerseyTest class. Going through the JerseyTest class I realized that it starts and stops the container for every test method using Junit's Before and After annotations.
Why is this necessary? Isn't it enough if we bring up the container once, run the tests and shut it down at the end of it?
We also use Spring and it takes time for the context to get initialized.
Prior to Junit4 we worked around this limitation by handling it manually using boolean flags.
@Before
public void setup() {
if(!containerStarted) {
// start
containerStarted = true;
}
// else do nothing
}
回答1:
You can use @BeforeClass and @AfterClass to override JerseyTest's @Before and @After lifecycle. Here is the code template:
public abstract class MyJerseyTest {
private JerseyTest jerseyTest;
public MyJerseyTest(){
}
@BeforeClass
public void setUp() throws Exception {
initJerseyTest()
jerseyTest.setUp();
}
@AfterClass
public void tearDown() throws Exception {
jerseyTest.tearDown();
}
private void initJerseyTest() {
jerseyTest = new JerseyTest() {
@Override
protected Application configure() {
// do somthing like
enable(...);
set(...);
// create ResourceConfig instance
ResourceConfig rc = new ResourceConfig();
// do somthing like
rc.property(...);
rc.register(...);
return rc;
}
};
}
}
Hope this is helpful.
回答2:
We had a similar situations, using jersey plus spring as dependency injection framework (jersey-spring-bridge). Writing integration test with JerseyTest
framework is tricky because it starts container before a test and stops the container after the test.
This approach might have an advantage but, its very time consuming and tricky taking into account the fact that spring does scanning and autowiring of beans everytime.
How to initialize a grizzly container once and use it for all tests in a test class?
To achieve the above, I followed the following steps:
In the test class as instance variable, declare an instance of HttpServer
as follow
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class OrderResourceTest {
...
public static final String BASE_URI = "http://localhost:8989/";
private static HttpServer server = null;
...
...
}
Notice that I do not use JerseyTest
because I want to handle the start/stop of test container myself. Now, you need to make use of @Before
and @AfterClass
to setup the server instance.
In @Before
we will setup the server
instance such that it load our custom filter/listener definitions in the web.xml
(like programmatically loading the web.xml into server
instance)
@Before
public void setUp() throws Exception {
if (server == null) {
System.out.println("Initializing an instance of Grizzly Container");
final ResourceConfig rc = new ResourceConfig(A.class, B.class);
WebappContext ctx = new WebappContext() {};
ctx.addContextInitParameter("contextConfigLocation", "classpath:applicationContext.xml");
ctx.addListener("com.package.something.AServletContextListener");
server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
ctx.deploy(server);
}
}
If you notice about I am using @Before but, the if condition will make it function as @BeforeClass. Don't exactly remember why I did not use @BeforeClass
and my guess would be probably due to some of those configurations inside the if block. Anyway, give it a try if you are curious.
- Create ResourceConfig with Resources that are going to be tested, this includes your Resource/Controller, ExceptionMapper if any, any other class that should be loaded.
- Create an instance of WebappContext and then programmatically add to it all the
web.xml
context init params such asapplicationContext
for example. WhereapplicationContext
contains spring-based configs. - If you have a listener in your web.xml, then you need to add them programmatically as shown above
- If you have any filters or something else then you need to programmatically add them to
ctx
. - Initialize
server
with Grizzly instance by providing it URI and instance ofResourceConfig
- You have already programmatically created a
WebappContext
which is nothing but, web.xml and now use it's deploy method to pass the server instance to it. This will run theWebappContext
configs and deploy it toserver
instance.
And now you have a test running an instance of Grizzly with your web.xml plus spring-specific configs applied to the instance.
The @AfterClass
could look as follow
@AfterClass
public static void tearDown() throws Exception {
System.out.println("tearDown called ...");
if (server != null && server.isStarted()) {
System.out.println("Shutting down the initialized Grizzly instance");
server.shutdownNow();
}
}
And a sample test using REST-assured framework
@Test
public void testGetAllOrderrs() {
List<Orders> orders= (List<Orders>)
when().
get("/orders").
then().
statusCode(200).
extract().
response().body().as(List.class);
assertThat(orders.size()).isGreaterThan(0);
}
The reason above works without specifying the base-path is because I set the REST-assured base path as follow
RestAssured.baseURI = "http://localhost:8989/";
If you didn't want to use REST-assured then
@Test
public void testGetAllOrders() {
Client client = ClientBuilder.newBuilder().newClient();
WebTarget target = client.target(BASE_URI);
Response response = target
.path("/orders")
.request(MediaType.APPLICATION_JSON)
.get();
assertThat(response.getStatus()).isEqualTo(200);
JSONArray result = new JSONArray(response.readEntity(String.class));
assertThat(result.length()).isGreaterThan(0);
}
Jersey-based imports
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
来源:https://stackoverflow.com/questions/38524672/how-to-start-a-jersey-test-container-grizzly-once-for-all-tests-in-a-test-clas