users@jersey.java.net

Re: [Jersey] GrizzlyWebTestContainer Test Suite Best Practices Inquiry

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 07 Jun 2010 14:06:25 +0200

On May 31, 2010, at 10:40 PM, Bartosz Bobnis wrote:

> Hello,
>
> > What version of Grizzly are you using? perhaps there is an issue in
> > Grizzly when shutting down?
>
> <properties>
> <jerseyVersion>1.3-SNAPSHOT</jerseyVersion>
> <!--jerseyVersion>1.1.5.1</jerseyVersion-->
> </properties>
> <dependency>
> <groupId>com.sun.jersey.jersey-test-framework</groupId>
> <artifactId>jersey-test-framework-grizzly</artifactId>
> <version>${jerseyVersion}</version>
> </dependency>
> <dependency>
> <groupId>com.sun.jersey.jersey-test-framework</groupId>
> <artifactId>jersey-test-framework-inmemory</artifactId>
> <version>${jerseyVersion}</version>
> </dependency>
>
> We were using 1.1.5.1 but I bumped it up to 1.3-SNAPSHOT where the
> problem (threads hanging afterwards) persisted.
>
> > > SelectorThreads are spun per object spawned by the Factory that
> are
> > > asked for @Before each resource test. That is, each test spins a
> > > thread and the "suite" falls on its face.
> > >
> >
> > The @After method will call this:
> >
> > public void stop() {
> > LOGGER.info("Stopping the Grizzly Web Container...");
> > webServer.stop();
> > webServer.getSelectorThread().stopEndpoint();
> > }
>
> I saw this as well but YourKit is showing threads lingering. Bumping
> the version to 1.3-SNAPSHOT after some pom massaging did not resolve
> this.
>

Can you confirm that the lingering threads are associated with
Grizzly? If so we can log a bug against Grizzly.


> > > Unfortunately, the InMemoryTestContainer (desirable: skips the
> HTTP
> > > portion, focuses on the resources, a lot quicker) does not "see"
> > > provider classes
> >
> > Can you describe a bit more about that?
>
> I'm not too familiar with the jersey test framework so hopefully
> this log output will better describe the problem I am experiencing:
>
> public class AbstractResourceTest extends JerseyTest {
> ...
> @Override
> protected AppDescriptor configure () {
> return new
> WebAppDescriptor
> .Builder
> ("com
> .primalfusion
> .platformapi
> .resource
> ").contextPath("platformApi").contextParam("contextConfigLocation",
> "classpath:platformApiTest-
> appContext
> .xml
> ").servletClass
> (SpringServlet
> .class).contextListenerClass(ContextLoaderListener.class).build();
> }
>
> @Override
> protected TestContainerFactory getTestContainerFactory () {
> return new GrizzlyWebTestContainerFactory();
> //return new InMemoryTestContainerFactory();
> }
> ...
> }
>
> Beginning output from using InMemoryTestContainerFactory() (more
> verbose error messaging than previous version that gives off a big
> hint about spring configuration (or lack thereof)):
>
> May 31, 2010 2:15:31 PM
> com.sun.jersey.api.core.PackagesResourceConfig init
> INFO: Scanning for root resource and provider classes in the packages:
> com.primalfusion.platformapi.resource
> May 31, 2010 2:15:31 PM
> com.sun.jersey.api.core.ScanningResourceConfig logClasses
> INFO: Root resource classes found:
> class com.primalfusion.platformapi.resource.SystemResource
> class com.primalfusion.platformapi.resource.UserDomainsResource
> class com.primalfusion.platformapi.resource.DomainConceptsResource
> class com.primalfusion.platformapi.resource.IndexResource
> class com.primalfusion.platformapi.resource.ActionsResource
> class com.primalfusion.platformapi.resource.DomainsResource
> class com.primalfusion.platformapi.resource.DomainOverviewResource
> class com.primalfusion.platformapi.resource.DomainResource
> class com.primalfusion.platformapi.resource.TestHelperResource
> class com.primalfusion.platformapi.resource.DomainConceptResource
> class com.primalfusion.platformapi.resource.DomainQueryResource
> May 31, 2010 2:15:31 PM
> com.sun.jersey.api.core.ScanningResourceConfig init
> INFO: No provider classes found.
> May 31, 2010 2:15:31 PM
> com
> .sun
> .jersey
> .test.framework.spi.container.inmemory.InMemoryTestContainerFactory
> $InMemoryTestContainer <init>
> INFO: Creating low level InMemory test container configured at the
> base URI http://localhost:9998/
> May 31, 2010 2:15:31 PM
> com
> .sun
> .jersey
> .test.framework.spi.container.inmemory.InMemoryTestContainerFactory
> $InMemoryTestContainer start
> INFO: Starting low level InMemory test container
> May 31, 2010 2:15:31 PM
> com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
> INFO: Initiating Jersey application, version 'Jersey: 1.3-SNAPSHOT
> 05/28/2010 12:13 AM'
> May 31, 2010 2:15:32 PM com.sun.jersey.spi.inject.Errors
> processErrorMessages
> SEVERE: The following errors have been detected with resource and/or
> provider classes:
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .ActionsResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainConceptResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainConceptsResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainOverviewResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainQueryResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .DomainsResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for field: javax.servlet.ServletContext
> com.primalfusion.platformapi.resource.SystemResource.context
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .SystemResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> SEVERE: Missing dependency for constructor public
> com
> .primalfusion
> .platformapi
> .resource
> .UserDomainsResource
> (com.primalfusion.platformapi.service.PlatformContext) at parameter
> index 0
> ...
>
> Beginning output from using GrizzlyWebTestContainerFactory():
>
> May 31, 2010 2:23:15 PM
> com
> .sun
> .jersey
> .test
> .framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory
> $GrizzlyWebTestContainer <init>
> INFO: Creating Grizzly Web Container configured at the base URI http://localhost:9998/platformApi
> May 31, 2010 2:23:16 PM
> com
> .sun
> .jersey
> .test
> .framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory
> $GrizzlyWebTestContainer start
> INFO: Starting the Grizzly Web Container...
> May 31, 2010 2:23:16 PM
> com.sun.grizzly.http.servlet.ServletContextImpl log
> INFO: Initializing Spring root WebApplicationContext
> 2010-05-31 14:23:17 INFO util.LoggingPropertyPlaceholderConfigurer
> - Loading properties file from class path resource
> [platform.properties]
> May 31, 2010 2:23:17 PM com.sun.grizzly.Controller logVersion
> INFO: Starting Grizzly Framework 1.9.18-i - Mon May 31 14:23:17 EDT
> 2010
> GET: /domains/8888/concepts/(1000000017,1000002071) (application/json)
> May 31, 2010 2:23:17 PM
> com.sun.jersey.api.core.PackagesResourceConfig init
> INFO: Scanning for root resource and provider classes in the packages:
> com.primalfusion.platformapi.resource
> May 31, 2010 2:23:17 PM
> com.sun.jersey.api.core.ScanningResourceConfig logClasses
> INFO: Root resource classes found:
> class com.primalfusion.platformapi.resource.DomainResource
> class com.primalfusion.platformapi.resource.DomainQueryResource
> class com.primalfusion.platformapi.resource.UserDomainsResource
> class com.primalfusion.platformapi.resource.ActionsResource
> class com.primalfusion.platformapi.resource.DomainConceptsResource
> class com.primalfusion.platformapi.resource.DomainsResource
> class com.primalfusion.platformapi.resource.TestHelperResource
> class com.primalfusion.platformapi.resource.DomainOverviewResource
> class com.primalfusion.platformapi.resource.DomainConceptResource
> class com.primalfusion.platformapi.resource.SystemResource
> class com.primalfusion.platformapi.resource.IndexResource
> May 31, 2010 2:23:17 PM
> com.sun.jersey.api.core.ScanningResourceConfig init
> INFO: No provider classes found.
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.servlet.SpringServlet getContext
> INFO: Using default applicationContext
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, DomainConceptsResource, of type
> com.primalfusion.platformapi.resource.DomainConceptsResource as a
> root resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, domainOverviewResource, of type
> com.primalfusion.platformapi.resource.DomainOverviewResource as a
> root resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, domainResource, of type
> com.primalfusion.platformapi.resource.DomainResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, indexResource, of type
> com.primalfusion.platformapi.resource.IndexResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, SystemResource, of type
> com.primalfusion.platformapi.resource.SystemResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, actionsResource, of type
> com.primalfusion.platformapi.resource.ActionsResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, domainQueryResource, of type
> com.primalfusion.platformapi.resource.DomainQueryResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, domainConceptResource, of type
> com.primalfusion.platformapi.resource.DomainConceptResource as a
> root resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, DomainsResource, of type
> com.primalfusion.platformapi.resource.DomainsResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, userDomainsResource, of type
> com.primalfusion.platformapi.resource.UserDomainsResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, webApplicationExceptionMapper, of
> type com.primalfusion.platformapi.util.WebApplicationExceptionMapper
> as a provider class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, unsupportedFormatExceptionMapper, of
> type
> com.primalfusion.platformapi.util.UnsupportedFormatExceptionMapper
> as a provider class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, illegalArgumentExceptionMapper, of
> type
> com.primalfusion.platformapi.util.IllegalArgumentExceptionMapper as
> a provider class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, defaultExceptionMapper, of type
> com.primalfusion.platformapi.util.DefaultExceptionMapper as a
> provider class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, WebApplicationExceptionMapper, of
> type com.primalfusion.platformapi.util.WebApplicationExceptionMapper
> as a provider class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.spi.spring.container.SpringComponentProviderFactory
> register
> INFO: Registering Spring bean, testHelperResource, of type
> com.primalfusion.platformapi.resource.TestHelperResource as a root
> resource class
> May 31, 2010 2:23:17 PM
> com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
> INFO: Initiating Jersey application, version 'Jersey: 1.3-SNAPSHOT
> 05/28/2010 12:13 AM'
> 2010-05-31 14:23:17 INFO service.PlatformContext - Creating new
> instance of class com.primalfusion.platformapi.service.PlatformContext
> ...
>
> > > (even though it sees the root resources via spring injected
> > > context.xml) out of the box so a quick TestContainerFactory swap
> > > won't work. I suspect differences between WebAppDescriptor and
> > > LowLevelDescriptor is at fault.
> > >
> >
> > Yes, there are some issues related to the transformation, it
> should be
> > more robust in reporting errors as it is not possible to map from
> web
> > to low level in all cases. But i am not sure the provider stuff is
> > related to this.
>
> Based on the log output above I'd say it has nothing to do with
> provider resources either since both GrizzlyWebTestContainer and
> InMemoryTestContainer both INFO: No provider classes found.
>
> > > My question is what are the best practices for writing test suites
> > > of this sort using the JerseyTest framework and JUnit 4? Currently
> > > the only annotations that are employed are @Test, @Before and
> > > @After. Ideally we'd like each test to be part of a suite of
> tests
> > > that has a WebTestContainer created at the beginning of the entire
> > > suite of tests rather than each individual test and have the
> > > WebAppDescriptor replaced @Before each individual @Test.
> > >
> >
> > Currently JerseyTest is a bit limited in this respect. I think
> what we
> > require is an AbstractJerseyTest from which you can extend and
> decide
> > on the policy of starting and stopping the test container.
>
> This sounds like an excellent idea!
>
> > I think the only way you can control this at the moment is to right
> > your own test container factory to control stop and start per test
> > class instantiation.
> >
> > Any thoughts Pavel?
>
> Other than the source provided in the jersey-test-framework itself
> are there any examples floating around the web of such customized
> TestContainerFactories?

No.


> A workaround idea was to have a factory that returns the same
> WebServer but resetting it each time.
>

Yes, that could be a good solution to work around until we fix stuff.

Paul.


> > As for a suite of tests that is be a little different to what we are
> > used to. If you can give an example of how you would like to write
> > your code we might be able to use that as a template to support that
> > style.
> >
> > Paul.
>
> Here are some test cases:
>
> @Test
> public void testGetJsonSucceeds()
> {
> ClientResponse response = callGet( path,
> MediaType.APPLICATION_JSON_TYPE );
> validateResponse( 200, MediaType.APPLICATION_JSON_TYPE, response );
> String body = response.getEntity( String.class );
> assertTrue( body.contains( validJson ) );
> }
>
> @Test
> public void testGetHtmlSucceeds()
> {
> ClientResponse response = callGet( path, MediaType.TEXT_HTML_TYPE );
> validateResponse( 200, MediaType.TEXT_HTML_TYPE, response );
>
> assertTrue( response.getEntity( String.class ).contains( "<td>Name</
> td><td>FE_Demo</td>" ) );
> }
>
> @Test
> public void testGetDefaultsToJson()
> {
> ClientResponse response = callGet( path, MediaType.WILDCARD_TYPE );
> validateResponse( 200, MediaType.APPLICATION_JSON_TYPE, response );
> }
>
> @Test
> public void testPutFails()
> {
> ClientResponse response = callPut( path, validJson,
> MediaType.APPLICATION_JSON_TYPE );
> assertEquals( 405, response.getStatus() );
> }
>
> These tests are very simple and specific, each testing a very narrow
> aspect of the PURL layer. The general form is: make one request to
> some URL, inspect the result, assert expectations. We want many of
> these simple, hyper-focused tests rather than a handful of
> complicated rambling ones.
>
> With hundreds or thousands of short tests, the time overhead
> incurred in setup and teardown per test is too costly (not to
> mention the environment leaks resources as it is cycled anew for
> each test).
>
> We'd like to take advantage of JUnit4's parameterized test framework
> that can operate on large cartesian products of test cases - this is
> simply not viable from the incurred overhead of jersey setting up
> and tearing down per test at the moment unfortunately using the
> GrizzlyWebTestContainer.
>
> Thanks again,
>
> Bartosz Bobnis
> Software Developer
> Primal