| <?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> |