| <?xml version="1.0"?> | |
| <!-- | |
| Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. | |
| This program and the accompanying materials are made available under the | |
| terms of the Eclipse Public License v. 2.0, which is available at | |
| http://www.eclipse.org/legal/epl-2.0. | |
| This Source Code may also be made available under the following Secondary | |
| Licenses when the conditions for such availability set forth in the | |
| Eclipse Public License v. 2.0 are satisfied: GNU General Public License, | |
| version 2 with the GNU Classpath Exception, which is available at | |
| https://www.gnu.org/software/classpath/license.html. | |
| SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | |
| --> | |
| <!DOCTYPE chapter [<!ENTITY % ents SYSTEM "jersey.ent" > %ents; ]> | |
| <chapter xmlns="http://docbook.org/ns/docbook" | |
| version="5.0" | |
| xml:lang="en" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| xmlns:xi="http://www.w3.org/2001/XInclude" | |
| xmlns:xlink="http://www.w3.org/1999/xlink" | |
| xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd | |
| http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd" | |
| xml:id="test-framework"> | |
| <title>Jersey Test Framework</title> | |
| <para> | |
| Jersey Test Framework originated as an internal tool used for verifying the correct implementation of | |
| server-side components. Testing RESTful applications became a more pressing issue with "modern" approaches like | |
| test-driven development and users started to look for a tool that could help with designing and running | |
| the tests as fast as possible but with many options related to test execution environment. | |
| </para> | |
| <para> | |
| Current implementation of Jersey Test Framework supports the following set of features: | |
| <itemizedlist> | |
| <listitem><para>pre-configured client to access deployed application</para></listitem> | |
| <listitem><para>support for multiple containers - grizzly, in-memory, jdk, simple, jetty</para></listitem> | |
| <listitem><para>able to run against any external container</para></listitem> | |
| <listitem><para>automated configurable traffic logging</para></listitem> | |
| </itemizedlist> | |
| Jersey Test Framework is primarily based on JUnit but you can run tests using TestNG as well. It works almost out-of-the | |
| box and it is easy to integrate it within your Maven-based project. While it is usable on all environments where you can | |
| run JUnit, we support primarily the Maven-based setups. | |
| </para> | |
| <section> | |
| <title>Basics</title> | |
| <programlisting language="java" linenumbering="numbered">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); | |
| } | |
| }</programlisting> | |
| <para> | |
| If you want to develop a test using Jersey Test Framework, you need to subclass &jersey.test.JerseyTest; and | |
| configure the set of resources and/or providers that will be deployed as part of the test application. This short | |
| code snippet shows basic resource class <literal>HelloResource</literal> used in tests defined as part of the | |
| <literal>SimpleTest</literal> class. The overridden <literal>configure</literal> method returns | |
| a &jersey.server.ResourceConfig; of the test application,that contains only the <literal>HelloResource</literal> | |
| resource class. &lit.jersey.server.ResourceConfig; is a sub-class of JAX-RS &jaxrs.core.Application;. It is a Jersey | |
| convenience class for configuring JAX-RS applications. &lit.jersey.server.ResourceConfig; also implements JAX-RS | |
| &jaxrs.core.Configurable; interface to make the application configuration more flexible. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Supported Containers</title> | |
| <para> | |
| &jersey.test.JerseyTest; supports deploying applications on various containers, all (except the external container | |
| wrapper) need to have some "glue" code to be supported. Currently Jersey Test Framework provides support for | |
| Grizzly, In-Memory, JDK (<literal>com.sun.net.httpserver.HttpServer</literal>), Simple HTTP container | |
| (<literal>org.simpleframework.http</literal>) and Jetty HTTP container (<literal>org.eclipse.jetty</literal>) | |
| - since Jersey 3.x HTTP Jetty container requires JDK 11+. | |
| </para> | |
| <para> | |
| A test container is selected based on various inputs. | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#getTestContainerFactory()">JerseyTest#getTestContainerFactory()</link> | |
| is always executed, so if you override it and provide your own version of | |
| &jersey.test.spi.TestContainerFactory;, nothing else will be considered. | |
| Setting a system variable | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/TestProperties.html#CONTAINER_FACTORY">TestProperties#CONTAINER_FACTORY</link> | |
| has similar effect. This way you may defer the decision on which containers you want to run your tests | |
| from the compile time to the test execution time. | |
| Default implementation of &lit.jersey.test.spi.TestContainerFactory; looks for container factories on classpath. | |
| If more than one instance is found and there is a Grizzly test container factory among them, it will be used; if not, | |
| a warning will be logged and the first found factory will be instantiated. | |
| </para> | |
| <para> | |
| Following is a brief description of all container factories supported in Jersey Test Framework. | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| Jersey provides 2 different test container factories based on Grizzly. | |
| The <literal>GrizzlyTestContainerFactory</literal> creates a container that can run as a light-weight, | |
| plain HTTP container. Almost all Jersey tests are using Grizzly HTTP test container factory. | |
| Second factory is <literal>GrizzlyWebTestContainerFactory</literal> that is Servlet-based and supports | |
| Servlet deployment context for tested applications. This factory can be useful when testing more complex | |
| Servlet-based application deployments. | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-grizzly2</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| In-Memory container is not a real container. It starts Jersey application and directly calls internal | |
| APIs to handle request created by client provided by test framework. There is no network communication | |
| involved. This containers does not support servlet and other container dependent features, but it is | |
| a perfect choice for simple unit tests. | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-inmemory</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| <literal>HttpServer</literal> from JDK is another supported test container. | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-jdk-http</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Simple container (<literal>org.simpleframework.http</literal>) is another light-weight HTTP container | |
| that integrates with Jersey and is supported by Jersey Test Framework. | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-simple</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Jetty container (<literal>org.eclipse.jetty</literal>) is another high-performance, light-weight HTTP server | |
| that integrates with Jersey and is supported by Jersey Test Framework. Shall be used along with JDK 11+. | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-jetty</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| </section> | |
| <section xml:id="testng"> | |
| <title>Running TestNG Tests</title> | |
| <para> | |
| It is possible to run not only JUnit tests but also tests based on TestNG. In order to do this you need to make sure | |
| the following 2 steps are fulfilled: | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| Extend &jersey.test.JerseyTestNg;, or one of its inner classes &jersey.test.JerseyTestNg.ContainerPerClassTest; | |
| / &jersey.test.JerseyTestNg.ContainerPerMethodTest;, instead of &jersey.test.JerseyTest;. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Add TestNG to your class-patch, i.e.: | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework</groupId> | |
| <artifactId>jersey-test-framework-core</artifactId> | |
| <version>&version;</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.testng</groupId> | |
| <artifactId>testng</artifactId> | |
| <version>...</version> | |
| </dependency></programlisting> | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| To discuss the former requirement in more depth we need to take a look at the differences between JUnit and TestNG. | |
| JUnit creates a new instance of a test class for every test present in that class which, from the point of view of | |
| Jersey Test Framework, means that new test container and client is created for each test of a test class. However, | |
| TestNG creates only one instance of a test class and the initialization of the test container depends more on | |
| setup/teardown methods (driven by <literal>@BeforeXXX</literal> and <literal>@AfterXXX</literal> annotations) than in | |
| JUnit. This means that, basically, you can start one instance of test container for all tests present in a test class | |
| or separate test container for each and every test. For this reason a separate subclasses of &jersey.test.JerseyTestNg; | |
| have been created: | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| &jersey.test.JerseyTestNg.ContainerPerClassTest; creates one container to run all the tests in. Setup method | |
| is annotated with <literal>@BeforeClass</literal>, teardown method with <literal>@AfterClass</literal>. | |
| </para> | |
| <para> | |
| For example take a look at <literal>ContainerPerClassTest</literal> test. It contains two test | |
| methods (<literal>first</literal> and <literal>second</literal>), one singleton resource that returns an | |
| increasing sequence of number. Since we spawn only one instance of a test container for the whole class the | |
| value expected in the first test is <literal>1</literal> and in the second it's <literal>2</literal>. | |
| <programlisting language="java" linenumbering="numbered">public class ContainerPerClassTest extends JerseyTestNg.ContainerPerClassTest { | |
| @Path("/") | |
| @Singleton | |
| @Produces("text/plain") | |
| public static class Resource { | |
| private int i = 1; | |
| @GET | |
| public int get() { | |
| return i++; | |
| } | |
| } | |
| @Override | |
| protected Application configure() { | |
| return new ResourceConfig(Resource.class); | |
| } | |
| @Test(priority = 1) | |
| public void first() throws Exception { | |
| test(1); | |
| } | |
| @Test(priority = 2) | |
| public void second() throws Exception { | |
| test(2); | |
| } | |
| private void test(final Integer expected) { | |
| final Response response = target().request().get(); | |
| assertEquals(response.getStatus(), 200); | |
| assertEquals(response.readEntity(Integer.class), expected); | |
| } | |
| }</programlisting> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| &jersey.test.JerseyTestNg.ContainerPerMethodTest; creates separate container for each test. Setup method | |
| is annotated with <literal>@BeforeMethod</literal>, teardown method with <literal>@AfterMethod</literal>. | |
| </para> | |
| <para> | |
| We can create a similar test to the previous one. Take a look at <literal>ContainerPerMethodTest</literal> | |
| test. It looks the same except the expected values and extending class: it contains two test | |
| methods (<literal>first</literal> and <literal>second</literal>), one singleton resource that returns an | |
| increasing sequence of number. In this case we create a separate test container for each test so | |
| value expected in the first test is <literal>1</literal> and in the second it's also <literal>1</literal>. | |
| <programlisting language="java" linenumbering="numbered">public class ContainerPerMethodTest extends JerseyTestNg.ContainerPerMethodTest { | |
| @Path("/") | |
| @Singleton | |
| @Produces("text/plain") | |
| public static class Resource { | |
| private int i = 1; | |
| @GET | |
| public int get() { | |
| return i++; | |
| } | |
| } | |
| @Override | |
| protected Application configure() { | |
| return new ResourceConfig(Resource.class); | |
| } | |
| @Test | |
| public void first() throws Exception { | |
| test(1); | |
| } | |
| @Test | |
| public void second() throws Exception { | |
| test(1); | |
| } | |
| private void test(final Integer expected) { | |
| final Response response = target().request().get(); | |
| assertEquals(response.getStatus(), 200); | |
| assertEquals(response.readEntity(Integer.class), expected); | |
| } | |
| }</programlisting> | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| If you need more complex setup of your test you can achieve this by directly extending the &jersey.test.JerseyTestNg; | |
| class create setup/teardown methods suited to your needs and provide a strategy for storing and handling a test | |
| container / client instance (see <literal>JerseyTestNg.configureStrategy(TestNgStrategy)</literal> method). | |
| </para> | |
| </section> | |
| <section> | |
| <title>Advanced features</title> | |
| <section> | |
| <title>&lit.jersey.test.JerseyTest; Features</title> | |
| <para>&lit.jersey.test.JerseyTest; provide | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#enable(java.lang.String)">enable(...)</link>, | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#forceEnable(java.lang.String)">forceEnable(...)</link> | |
| and <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#disable(java.lang.String)">disable(...)</link> | |
| methods, that give you control over configuring values of the properties defined and described in the | |
| &lit.jersey.test.TestProperties; class. A typical code that overrides the default property values is listed | |
| below: | |
| <programlisting language="java" linenumbering="numbered">public class SimpleTest extends JerseyTest { | |
| // ... | |
| @Override | |
| protected Application configure() { | |
| enable(TestProperties.LOG_TRAFFIC); | |
| enable(TestProperties.DUMP_ENTITY); | |
| // ... | |
| } | |
| }</programlisting> | |
| The code in the example above enables test traffic logging (inbound and outbound headers) as well as | |
| dumping the HTTP message entity as part of the traffic logging. | |
| </para> | |
| </section> | |
| <section> | |
| <title>External container</title> | |
| <para> | |
| Complicated test scenarios may require fully started containers with complex setup configuration, that is not | |
| easily doable with current Jersey container support. To address these use cases, Jersey Test Framework providers | |
| general fallback mechanism - an External Test Container Factory. Support of this external container "wrapper" is | |
| provided as the following module: | |
| <programlisting language="xml"><dependency> | |
| <groupId>org.glassfish.jersey.test-framework.providers</groupId> | |
| <artifactId>jersey-test-framework-provider-external</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| As indicated, the "container" exposed by this module is just a wrapper or stub, that redirects all request to | |
| a configured host and port. Writing tests for this container is similar to any other but you have to provide | |
| the information about host and port during the test execution: | |
| <screen>mvn test -Djersey.test.host=myhost.org -Djersey.config.test.container.port=8080</screen> | |
| </para> | |
| </section> | |
| <section> | |
| <title>Test Client configuration</title> | |
| <para> | |
| Tests might require some advanced client configuration. This is possible by overriding | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#configureClient(org.glassfish.jersey.client.ClientConfig)">configureClient(ClientConfig clientConfig)</link> | |
| method. Typical use case for this is registering more providers, such as &jaxrs.ext.MessageBodyReader;s or | |
| &jaxrs.ext.MessageBodyWriter;s, or enabling additional features. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Accessing the logged test records programmatically</title> | |
| <para> | |
| Sometimes you might need to check a logged message as part of your test assertions. For this purpose Jersey Test | |
| Framework provides convenient access to the logged records via | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#getLastLoggedRecord()">JerseyTest#getLastLoggedRecord()</link> | |
| and | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/JerseyTest.html#getLoggedRecords()">JerseyTest#getLoggedRecords()</link> | |
| methods. Note that this feature is not enabled by default, see | |
| <link xlink:href="&jersey.javadoc.uri.prefix;/test/TestProperties.html#RECORD_LOG_LEVEL">TestProperties#RECORD_LOG_LEVEL</link> | |
| for more information. | |
| </para> | |
| </section> | |
| </section> | |
| <section xml:id="parallel"> | |
| <title>Parallel Testing with Jersey Test Framework</title> | |
| <para> | |
| For a purpose of running multiple test containers in parallel you need to set the | |
| <link xlink:href='&jersey.javadoc.uri.prefix;/test/TestProperties.CONTAINER_PORT.html'>TestProperties.CONTAINER_PORT</link> | |
| to <literal>0</literal> value. This will tell Jersey Test Framework (and the underlying test container) to use the | |
| first available port. | |
| </para> | |
| <para> | |
| You can set the value as a system property (via command line option) or directly in the test (to not affect ports of | |
| other tests): | |
| <programlisting language="java" linenumbering="numbered">@Override | |
| protected Application configure() { | |
| // Find first available port. | |
| forceSet(TestProperties.CONTAINER_PORT, "0"); | |
| return new ResourceConfig(Resource.class); | |
| }</programlisting> | |
| </para> | |
| <para> | |
| The easiest way to setup your JUnit or TestNG tests to run in parallel is to configure Maven Surefire plugin. You can | |
| do this via configuration options <literal>parallel</literal> and <literal>threadCount</literal>, i.e.: | |
| <programlisting language="xml">... | |
| <configuration> | |
| <parallel>methods</parallel> | |
| <threadCount>5</threadCount> | |
| ... | |
| </configuration> | |
| ...</programlisting> | |
| For more information about this topic consult the following Maven Surefire articles: | |
| <itemizedlist> | |
| <listitem> | |
| <para><link xlink:href="http://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html">Fork Options and Parallel Test Execution</link></para> | |
| </listitem> | |
| <listitem> | |
| <para><link xlink:href="http://maven.apache.org/surefire/maven-surefire-plugin/examples/testng.html#Running_tests_in_parallel">Using TestNG - Running tests in parallel</link></para> | |
| </listitem> | |
| <listitem> | |
| <para><link xlink:href="http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html#Running_tests_in_parallel">Using JUnit - Running tests in parallel</link></para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| </section> | |
| </chapter> |