Jersey Test Framework re-visited!

   One of the previous entries introduced the Jersey Test Framework, which has since been adopted and used by a good number of developers. However, there has been some feedback suggesting ways for making the framework a better one.

Based on all this feedback, we have worked on making some changes in the framework. With the release of Jersey 1.1.2-ea, we have this new version of the framework which is better than the previous version in the following ways:

  • Introduced the concept of test container factories
  • Various test container types, defined by the different test container factory implementations
  • Support for the new In-Memory or In-Process test container
  • Loosely-coupled with the test container factory implementations
  • Loose coupling allows the definition and pluggability of custom test container factory implementations
  • Support for running tests on an application pre-deployed on an external container

But, there have been some major changes in the API, which seemed obvious for the cause.

This entry will describe what are the API changes, and how an user test can be defined, etc.

Breaking changes from 1.1.1-ea to 1.1.2-ea

  • The maven project groupId has changed from “com.sun.jersey.test.framework” to “com.sun.jersey”. 
                <dependency>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-test-framework</artifactId> 
                    <version>1.1.2-ea</version>
                <dependency>
  • The extending of Jersey unit test and configuration has changed.

                   The test class has to just pass an instance of AppDescriptor. For instance, the constructor of the spring-annotations sample test, passes this information as follows:

 public SpringAnnotationsWebAppTest() throws Exception {
      super(new WebAppDescriptor.Builder("com.sun.jersey.samples.springannotations.resources.jerseymanaged")
                .contextPath("spring")
                .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
                .servletClass(SpringServlet.class)
                .contextListenerClass(ContextLoaderListener.class)
                .build());
    }

           Note the use of the Builder design pattern, which makes it really easy to define an instance of the AppDescriptor while defining all the application attributes.

  • The test container type with which to run the tests has to be specified using the System Property test.containerFactory. Note that it used to be container.type till the previous version.
  • Unlike the previous implementation, the test container type value is not a string which maps to the container type, but the fully qualified class name of the test container factory is passed as value for the property test.containerFactory.
  •    mvn test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory 
    

About the AppDescriptor

            AppDescriptor is an abstract class which is extended by two classes - the LowLevelAppDescriptor and the WebAppDescriptor. These classes allow the definition of the various attributes of the application - like its context-path, url-pattern, root resource classes or packages, etc. While the LowLevelAppDescriptor can be used is cases were tests are to be run on light-weight containers like Grizzly or HTTPServer, the WebAppDescriptor is used in cases where tests could be run on the web-based containers like EmbeddedGlassFish, Grizzly Web Container, and the light-weight containers as well\*.

Test Container Factories

       The test framework comes with a set of test container factory implementations which are responsible for creating the test container(s).

The following low-level test container factories are provided:

The following Web-based test container factories are provided:

Running Tests using Maven

      As previously said, the container on which the tests have to be run is specified using the system property test.containerFactory which holds the fully-qualified classname of the test container factory which creates an instance of the test container, i.e.,

 
  
          mvn clean test -Dtest.containerFactory=<container-factory fully-qualified class name>

      Note:

           1. If tests are to be run on external container like GlassFish, the application has to be explicity deployed on the container before running the tests.

               a. Package the application:

               mvn clean package -Dmaven.test.skip=true

               b. Deploy the generated application war file 

               c. Run the tests:

              mvn test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory

           2. If the  tests are to be run on EmbeddedGlassFish, one additional property container.type has to be set along with test.containerFactory:

              mvn clean test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.embedded.glassfish.EmbeddedGlassFishTestContainerFactory -Dcontainer.type=EmbeddedGF 

           3. If the property test.containerFactory is not set, the tests would be run on the Grizzly Web container by default.

 
  

Enable Logging

The framework allows the logging of the HTTP requests and responses being sent over the wire during the test process. All that is needed to enable this logging is set the flag enableLogging.

           mvn clean test -Dtest.containerFactory=<test container factory class> -DenableLogging

Programmatically setting the test container factory

      The framework also allows setting the test container factory programmatically. This could be done by overriding the JerseyTest class's getTestContainerFactory method and returning the appropriate test container factory's instance. For example if Grizzly Web container has to be set as the default test container factory, it could be done as follows:

    @Override
    protected TestContainerFactory getTestContainerFactory() {
        return new GrizzlyWebTestContainerFactory();
    }

                       That's a brief description of the new version of the Jersey Test Framework. Please send an email to the Jersey user's mailing list users@jersey.dev.java.net  in case you have any issues. Wish you a happy testing of your RESTful Web Services :)       

Comments:

There seems to be a problem with maven artifact "jersey-test-framework".

<dependency>

<groupId>com.sun.jersey</groupId>

<artifactId>jersey-test-framework</artifactId>

<version>1.1.2-ea</version>

<dependency>

This does not download. Rest of the jersey jars download fine for 1.1.2-ea.

Posted by spulla on September 01, 2009 at 09:17 AM PDT #

Hi,

I'm able to use it without any issue.

The artifacts are available at the download.java.net site too -
http://download.java.net/maven/2/com/sun/jersey/jersey-test-framework/1.1.2-ea/

BTW, what repo are you using?

Do you have the following as one of the repositories:

<repository>
<id>m2.dev.java.net</id>
<url>http://download.java.net/maven/2</url>
<layout>default</layout>
</repository>

Posted by Naresh on September 01, 2009 at 06:22 PM PDT #

Hi Naresh,

Thanks for your response. I used the repo url you gave me. The good news is I was able download test framework but fails on org.glassfish.embedded:glassfish-embedded-all:jar:3.0-Prelude-Embedded-b14. (Transitive dependency).

Obviously I could download manually but I don't want to, for my scenario. I was writing some automated tests for my project so wanted to experiment with this version library.

sp

Posted by spulla on September 02, 2009 at 05:32 AM PDT #

Hi Naresh,

Ignore my previous comment. I got everything now. Thanks.

-sp

Posted by guest on September 02, 2009 at 06:08 AM PDT #

Hi Spulla,

great to know that you got the framework working now.
Please feel free to send a mail to the users mailing list users@jersey.dev.java.net, in case you run into any issues.

Posted by Naresh on September 02, 2009 at 03:08 PM PDT #

I am using "GrizzlyWebTestContainerFactory (jersey-test-framework-1.1.2-ea.jar)" to write testcases for my jersey service which uses session variable.
I am not sure how to make available session objects through test framework?
Tried to set session objects using filterClass (WebAppDescriptor.Builder) but it shows me null pointer exception while starting container. I gone through the src code and found this chunk causing error:
try {
servletInstance = (Servlet) servletClass.newInstance();
} catch (InstantiationException ex) {
throw new TestContainerException(ex);
} catch (IllegalAccessException ex) {
It always excepting servletClass and when I setting filter class this code snippet sets this.servletClass = null and one more thing test framework not using filter class anywhere.

try {
servletInstance = (Servlet) servletClass.newInstance();
} catch (InstantiationException ex) {
throw new TestContainerException(ex);
} catch (IllegalAccessException ex) {public Builder filterClass(Class<? extends Filter> filterClass)
throws IllegalArgumentException {
if (filterClass == null)
throw new IllegalArgumentException("The filter class must not be null");
this.filterClass = filterClass;
this.servletClass = null;
return this;
}

Thanks,
Yogesh

Posted by Yogesh Kumar on September 11, 2009 at 06:53 PM PDT #

Hi Yogesh,

the framework not using the filter class was indeed a bug in the implementation, which I had overseen during the development. However, we have identified this after the release of 1.1.2-ea, and fixed it in 1.1.3-ea-SNAPSHOT. Can you please try using the 1.1.3-ea-SNAPSHOT version?

One more thing is, by design we have made the use of servlet or filter mutually exclusive.

Please let us know, if things are working for you with the current implementation or not.

Posted by Yogesh on September 12, 2009 at 03:50 AM PDT #

With 1.1.3-ea-SNAPSHOT version filter is getting executed but not forwarding request to requested resource.

My motivation to use filter here is to make available session objects to my jersey service layer.

Is there any other way to make available session objects to my jersey service through test framework?

Posted by guest on September 12, 2009 at 05:44 AM PDT #

I'm using Jersey-RS 1.0 in netbeans 6.5 without maven.
I'm deploying to tomcat.
Any idea which jersey-test-framework version I should use?
Also, which grizzly jar file versions should I use with the given test framework version?

Thanks,
Brian

Posted by Brian on September 30, 2009 at 09:23 AM PDT #

Nevermind, I just made a maven project and and let it tell me the dependencies. Now I can drop them into my existing project.

Posted by Brian on September 30, 2009 at 01:23 PM PDT #

Great.
I hope you are using the latest version, i.e., 1.1.2-ea
Please let me know if you face any issues.

I shall soon add the links to the required jar files for non-maven users.

Posted by Naresh on September 30, 2009 at 04:56 PM PDT #

Any idea why I get this error?

java.lang.NoSuchMethodError: com.sun.grizzly.tcp.http11.GrizzlyAdapterChain.setDecodeUrl(Z)V

Using Jersey 1.0 that comes with NetBeans 6.5 plus the following jars:
grizzly-framework-1.9.8.jar
grizzly-http-1.9.8.jar
grizzly-http-servlet-1.9.8.jar
grizzly-portunif-1.9.8.jar
grizzly-rcm-1.9.8.jar
grizzly-servlet-webserver-1.9.8.jar
grizzly-utils-1.9.8.jar
jersey-test-framework-1.0.3.jar

Posted by Brian on October 01, 2009 at 12:05 AM PDT #

I just saw your other comment as well. So would 1.1.2-ea work with Jersey 1.0.3?

Posted by guest on October 01, 2009 at 12:07 AM PDT #

First;
Is it possible to supply static resources (like html-files) to be served by the test web server along with the resources?

I have some 'not so clever' forwarding/redirects to static content in my rest resources that I need to test. But all I get is 404 on all the static resource I try to access. Everything works fine if I deploy the app.

simplfied resource;
@GET
public Response get() throws URISyntaxException {
return Response.temporaryRedirect(new URI("/index.html")).build();
}

Sounds like I missed something easy, or?

I'm using 1.1.3-ea, I need that filter fix namned above for my custom filters.

I've tried all of the containers (excluding ExternalTestContainerFactory which is no option and EmbeddedGlassFishTestContainerFactory which crashes hard)

And the seconds question;
Is it possible to serve jsp files (with custom Simple Tag Handler's) together with the resources aswell?

Posted by maq on October 01, 2009 at 12:31 AM PDT #

Brian,

that NoSuchMethodError indicates that its possibly because of multiple versions of Grizzly jars being present. Since, you are trying with JerseyTestFramework v 1.0.3, can you please use the Jersey jars of version 1.0.3 as well?

Coming to your other question of whether the test framework v1.1.2-ea would work with Jersey v1.0.3 - I doubt it would because the test framework has dependency on Jersey v1.1.2-ea. If you want to use the test framework v1.1.2-ea, you might want to consider upgrading to Jersey 1.1.2-ea (if its possible)?

I thought you were trying to run your tests on Tomcat. If its so, you will have to do the following for running the tests against Tomcat (an External Container Type, as per the framework's design):

1. Deploy your application on Tomcat.
2. If you are using JTF v1.0.3, set the property "container.type" to "External" while running the tests, and also set "JERSEY_HTTP_PORT" to your Tomcat instance's configured HTTP port, which usually would be "8080".
3. On the other hand if you are running the tests using JTF v1.1.2-ea, set the property "test.containerFactory" to "com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory", and also set "JERSEY_HTTP_PORT" according to your Tomcat configuration.

JTF --> Jersey Test Framework

Posted by Naresh on October 01, 2009 at 01:25 AM PDT #

Maq,

firstly, regarding the static HTML pages, currently the light-weight test containers like Grizzly or HTTPServer would not be able to do that, because all that the current implementation does is scan for resources using the configurations mentioned in the test. However, I think the EmbeddedGlassFish could be helpful in this case.
When you say, it crashes bad time, can you please let me know what really happens? May be a dump of the stack trace could be helpful (I'm assuming that in this case you had also set the property "container.type" to "EmbeddedGF", in addition to setting the "test.containerFactory" property).

Second, for the JSPs too, similar to the above, I think EmbeddedGlassFish test container type should be helpful. Might be the GrizzlyWeb test container type could be modified to do something like that as well (I will have to investigate on that).

Posted by Naresh on October 01, 2009 at 01:36 AM PDT #

Naresh,

Thanks for the reply. I worked around the grizzly problem by using the lightweight http container. At this point I just want working unit tests, I don't really care about testing in multiple containers.

As far as the no such method error, I suspect NB 6.5 is providing jersey 1.0 where 1.0 < 1.0.3, so that must be my issue. Due to the way netbeans hooks into jersey (e.g. creating a restful web service automatically ads jersey to the project dependencies if it isn't already there), I'm not sure how to upgrade jersey versions without upgrading NB, and I'm not ready to do that yet.
Thanks

Posted by Brian on October 01, 2009 at 05:32 AM PDT #

I'm trying to figure out how to pass queryParams from the test framework.

This doesn't work:
String responseMsg = webResource.path("message/55?name=Tom").get(String.class);

I think I want to do:
webResource.getBuilder().queryParam("name", "Tom").path("messages/55"), but this returns a UriBuilder or a URI and I'm not sure how to get from either to a webResource.

Posted by Brian on October 06, 2009 at 01:10 AM PDT #

Ok, I just answered my own question.

URI uri = webResource.getBuilder().queryParam("name", "Tom").path("messages/{a}").build("55");
String responseMsg = webResource.uri(uri).get(String.class);

Posted by guest on October 06, 2009 at 01:34 AM PDT #

Great to hear that you got it working :)

Posted by Naresh on October 06, 2009 at 02:01 AM PDT #

Note that to set the testcontainer in code, extend JerseyTest.configure():

protected AppDescriptor configure() {
this.setTestContainerFactory(new com.sun.jersey.test.framework.spi.container.inmemory.InMemoryTestContainerFactory());
return new WebAppDescriptor.Builder("com.example.web")
.contextPath("/").build(); }

Posted by Tarjei Huse on November 08, 2009 at 08:11 PM PST #

Hi Tarjei,

you could definitely use the code fragment that you had mentioned for configuring the default test container factory.

Alternatively, the following code fragment could be used too:

@Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}

Posted by Naresh on November 08, 2009 at 10:23 PM PST #

If I override the getTestCotainerFactory with InMemoryTestContainerFactory
return new InMemoryTestContainerFactory();
I get a null pointer exception for method

webResource.path(URLPATH).get(String.class)

I am using 1.1.4.1 jersey test framework. Shouldn't I be using WebResource for InMemoryTestContainer?
---
java.lang.NullPointerException
at com.sun.jersey.server.impl.application.WebApplicationImpl.mapException(WebApplicationImpl.java:973)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:662)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:612)
at com.sun.jersey.test.framework.impl.container.inmemory.TestResourceClientHandler.handle(TestResourceClientHandler.java:116)
at com.sun.jersey.api.client.Client.handle(Client.java:397)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:179)
......

Posted by Prakash on January 05, 2010 at 04:57 PM PST #

Missing artifact org.glassfish.embedded:glassfish-embedded-all:jar:3.0-Prelude-Embedded-b14:test

In my attempt to use jersey test frame work, I included it in my maven 2 pom file as a dependency.

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-test-framework</artifactId>
<version>1.1.5.1</version>
<scope>test</scope>
</dependency>

The repository entries in my POM are as follows

<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository>
<repository>
<id>maven-repository.dev.java.net</id>
<name>Java.net Maven 1 Repository (legacy)</name>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
<repository>
<id>glassfish-repository</id>
<name>Java.net Repository for Glassfish</name>
<url>http://download.java.net/maven/glassfish</url>
</repository>
</repositories>

I followed the instructions in https://jersey.dev.java.net/nonav/documentation/latest/user-guide.html#d4e1302 and still I believe I am missing something as the I am encountering the error

Missing artifact org.glassfish.embedded:glassfish-embedded-all:jar:3.0-Prelude-Embedded-b14:test

Any help in this regards will be highly appreciated.

Posted by Viswanath on April 28, 2010 at 08:02 PM PDT #

Viswanath,

the issue has been discussed pretty recently in the Jersey users mailing list [1]. Looks like the version 2.0 of the framework would get rid of this problem.

[1] http://markmail.org/search/?q=EmbeddedGlassfishTestContainerFactory%20in%20Jersey%20test%20framework

Posted by Naresh on April 28, 2010 at 08:11 PM PDT #

Hi,
I have multiple Test classes, however running 'mvn test' runs only first test. How can I make it run all the tests

Posted by Raj on October 07, 2010 at 05:40 AM PDT #

confirmed Prakash's NPE problem - any ideas?

Posted by ian smith on October 11, 2010 at 04:45 AM PDT #

after two days of messing in vain with setup & maven dependencies, trying to get my project to work, and also trying to get the helloworld sample to fail, I have finally solved this . . .

there seems to be a problem with JUnit setup, when I commented out the method

@Before
public void setUp() throws Exception {
}

in my tests, the framework just worked. Looks like a bug to me . . .

Posted by ian smith on October 11, 2010 at 07:38 PM PDT #

Ian,

could you please log an issue or send an email to the Jersey user alias - users@jersey.dev.java.net?

I'm sure someone from the team would fix the problem.

Posted by Naresh on October 11, 2010 at 07:44 PM PDT #

Raj,

I was able to reproduce the problem you mentioned.
Looks like this happens when the test class name doesn't end with the suffix "Test".

For instance, in the helloworld-webapp sample, I copied the test class HelloWorldWebappTest.java to HelloWorldWebappTest2.java, and then did a "mvn clean test". This test run did not consider the the other test class "HelloWorldWebappTest2". However, when I refactored that class to HelloWorld2WebappTest.java, "mvn clean test" did run the tests from both the classes.

Probably, this is an issue with Maven's way of finding tests.

HTH.

Posted by Naresh on October 11, 2010 at 07:46 PM PDT #

Naresh, no offence to you personally, but I am really starting to lose patience with this framework ATM and am not inclined to help further UNTIL I actually see something working. I'm sure the devs are capable of duplicating my problem as stated on this blog.

My latest problem is that my REST services are injected with service EJBs, unfortunately under the Jersey Test Framework they are not, so I am back to NPE land. <rant>What exactly is the problem with documenting this thing properly? Seems to me it promises much but delivers very little.</rant>

Anyway, off to Google to see if what I want is actually possible, otherwise it's back to HTMLUnit . . . hopefully I will return later with a more positive outlook . . .

Ian.

Posted by ian smith on October 11, 2010 at 09:31 PM PDT #

It seems I am not alone . . .

http://forums.java.net/jive/thread.jspa?threadID=153556&tstart=45

BTW I am NOT using CDI - no beans.xml, I learned that lesson early on the hard way.

Not sure there is much more I can do for now, as far as I can tell it is currently not possible to test resources containing injected EJBs using this framework.

Regards, Ian.

Posted by ian smith on October 11, 2010 at 10:25 PM PDT #

OK, I should just reiterate, my last comment was regarding the in-memory test container. Ian

Posted by ian smith on October 12, 2010 at 06:51 PM PDT #

email sent to users list as requested . . . Ian.

Posted by ian smith on October 12, 2010 at 07:28 PM PDT #

Could you please post examples which do not use Maven? Not everyone who uses Jersey uses Maven.

Posted by Jared on November 22, 2010 at 02:37 AM PST #

nice article Naresh and it saved me lot of time in implementing test framework for my app

Posted by Rajender on January 20, 2011 at 07:10 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Naresh worked at Sun Microsystems for two years. During these two years he had worked on the Project Metro and Project Jersey.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today