开发者

Testing Jersey-Spring Integration with JerseyTest, Maven and TestNG

I want to test my Jersey resources with the Jersey Test-Framework.

I followed the descriptions provided here

  • http://blogs.oracle.com/naresh/entry/jersey_test_framework_makes_it
  • http://zhanghaoeye.javaeye.com/blog/441759

to create a simple example. My example is hosted as git repository on http://github.com/rmetzler/Jersey-Test .

$ mvn jetty:run works as expected but I keep getting NullPointerExceptions when running $ mvn clean test.

java.lang.NullPointerException
at com.sun.jersey.spi.container.ContainerResponse.mapException(ContainerResponse.java:429)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1295)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1239)
at com.sun.jersey.test.framework.impl.container.inmemory.TestResourceClientHandler.handle(TestResourceClientHandler.java:119)
at com.sun.jersey.api.client.Client.handle(开发者_Go百科Client.java:616)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:559)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:182)
at example.jersey.spring.MyResourceTest.testMyResource(MyResourceTest.java:30)
...

I bet I made a small mistake that I'm unable to find. I would show my source to another developer but unfortunately I work alone at home. So maybe someone of you could help me?

UPDATE

I created an Eclipse project by running $ mvn eclipse:eclipse . Now when I run the test as JUnit Test in Eclipse it is green. When running it as TestNG Test it fails. So I guess it has something to do with how the test is executed by TestNG.


I did the same thing except for I am using guice, not spring. This is my implementation (sorry, no time to clean up, you'll have to extract the interesting parts yourself). Note that I used a delegate jerseytest class so I can inherit some code from my base test class. Also you have to map the junit pre- and post-methods to testng ones.

Hope this helps a bit.


package mypackage;

import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.commons.lang.UnhandledException;
import org.apache.xerces.dom.DOMInputImpl;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.FunctionObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.w3c.dom.Document;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.staticMethod;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.WebApplication;
import com.sun.jersey.spi.container.WebApplicationFactory;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.LowLevelAppDescriptor;
import com.sun.jersey.test.framework.impl.container.inmemory.TestResourceClientHandler;
import com.sun.jersey.test.framework.spi.container.TestContainer;
import com.sun.jersey.test.framework.spi.container.TestContainerException;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import com.sun.jersey.test.framework.spi.container.inmemory.InMemoryTestContainerFactory;

import mypackage.StaticConfig;
import mypackage.MediaTypes;

public abstract class JerseyIntegrationTestBase extends TransactionalIntegrationTestBase {

    private static final Logger LOG = LoggerFactory.getLogger( JerseyIntegrationTestBase.class );

    private static final class GuiceInMemoryTestContainerFactory extends InMemoryTestContainerFactory {

        @Override
        public TestContainer create( final URI baseUri, final AppDescriptor ad ) {
            return new GuiceInMemoryTestContainer( baseUri, (LowLevelAppDescriptor) ad );
        }

        /**
         * Kopie der Klasse im inmemory-Testcontainer von Jersey, leicht an Guice-Injection angepasst. The class defines methods for
         * starting/stopping an in-memory test container, and for running tests on the container.
         */
        private static class GuiceInMemoryTestContainer implements TestContainer {

            final URI baseUri;

            final ResourceConfig resourceConfig;

            final WebApplication webApp;

            /**
             * Creates an instance of {@link GuiceInMemoryTestContainer}
             * 
             * @param Base
             *            URI of the application
             * @param An
             *            instance of {@link LowLevelAppDescriptor}
             */
            private GuiceInMemoryTestContainer( final URI baseUri, final LowLevelAppDescriptor ad ) {
                this.baseUri = UriBuilder.fromUri( baseUri ).build();

                LOG.info( "Creating low level InMemory test container configured at the base URI " + this.baseUri );

                resourceConfig = ad.getResourceConfig();
                // Falls man mal in Tests die requests und responses sehen möchte:
                // this.resourceConfig.getProperties().put( ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
                // LoggingFilter.class.getName() );
                // this.resourceConfig.getProperties().put( ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
                // LoggingFilter.class.getName() );
                resourceConfig.getProperties().putAll( StaticConfig.getJerseyParams() );
                webApp = WebApplicationFactory.createWebApplication();
            }

            @Override
            public Client getClient() {
                ClientConfig clientConfig = null;
                final Set providerSingletons = resourceConfig.getProviderSingletons();

                if ( providerSingletons.size() > 0 ) {
                    clientConfig = new DefaultClientConfig();
                    for ( final Object providerSingleton : providerSingletons ) {
                        clientConfig.getSingletons().add( providerSingleton );
                    }
                }

                final Client client = clientConfig == null
                    ? new Client( new TestResourceClientHandler( baseUri, webApp ) )
                    : new Client( new TestResourceClientHandler( baseUri, webApp ), clientConfig );

                return client;
            }

            @Override
            public URI getBaseUri() {
                return baseUri;
            }

            @Override
            public void start() {
                if ( !webApp.isInitiated() ) {
                    LOG.info( "Starting low level InMemory test container" );
                    webApp.initiate( resourceConfig, new GuiceContainer( null ).new ServletGuiceComponentProviderFactory(
                            resourceConfig, IntegrationTestBase.getInjector() ) );
                }
            }

            @Override
            public void stop() {
                if ( webApp.isInitiated() ) {
                    LOG.info( "Stopping low level InMemory test container" );
                    webApp.destroy();
                }
            }

        }
    }

    private final JerseyTest _jerseyTest;

    public JerseyIntegrationTestBase() {

        // PackagesResourceConfig.getPackages macht genau das, was wir wollen, ist aber private, daher
        // auf die harte Tour...
        // FORMATTER: OFF
        final String[] packages =
                staticMethod( "getPackages" ).withReturnType( String[].class ).withParameterTypes( Map.class )
                    .in( PackagesResourceConfig.class ).invoke( StaticConfig.getJerseyParams() );
        // FORMATTER: ON
        _jerseyTest = new JerseyTest( new LowLevelAppDescriptor.Builder( packages ).build() ) {
            @Override
            protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
                return new GuiceInMemoryTestContainerFactory();
            }
        };
    }

    /**
     * @return
     * @see JerseyTest#client().
     */
    protected Client client() {
        return _jerseyTest.client();
    }

    @BeforeClass( alwaysRun = true )
    public void setUp() throws Exception {
        _jerseyTest.setUp();
    }

    @AfterClass( alwaysRun = true )
    public void tearDown() throws Exception {
        _jerseyTest.tearDown();
    }
}


Because JerseyTest is using @Before annotation from Junit for initialising the application, and you have to extend JerseyTest to enable testng support, like this:

public class JerseyTestNG extends JerseyTest { 
    @Override
    protected Application configure() {
        ResourceConfig config = new ResourceConfig(YourService.class);
    }

    @BeforeClass 
    public void setUp() {
        super.setUp();
    }

    @AfterClass
    public void tearDown() {
        super.tearDown();
    }
}

@BeforeClass also will make sure all tests within are executed after the Jersey container is ready when using surefire plugin. Otherwise those tests will fail quickly.

and if you want to it read applicationContext-test.xml other than the default one, set one more property to ResourceConfig:

config.setProperties(new HashMap<String, String>() {{
    put("contextConfigLocation", "applicationContext-test.xml");
}});

also, adding some features to ClientConfig maybe helpful:

@Override
protected void configureClient(ClientConfig config) {
    config.register(LoggingFilter.class);
    config.register(MOXyJsonProvider.class);
    config.register(new EncodingFeature(GZipEncoder.class));
}

All tested on Jersey 2.6.


A simple example using Jersey + Spring + TestNG + Jetty here:

http://ameethpaatil.blogspot.com/2011/09/restful-webservice-jersey-spring-30x.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜