1

When I run my Maven tests with the Jersey Test Framework for a class with @Path, it works fine. But when i try to test a @WebServlet class, it does not and says it's a 404 error.

how do I test a web servlet with Jersey Test Framework? (Or, if this isn't possible, how else can i test this?)

Web Servlet:

package com.testservlet;


@WebServlet("/test")
public class TestServlet extends HttpServlet
{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        resp.getWriter().println( "The Result" );
    }
}

Test class:

import com.testservlet.TestServlet;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;

import javax.ws.rs.core.Application;

public class TestEndpoint extends JerseyTest
{
    @Override
    protected Application configure()
    {
        return new ResourceConfig( JPAServlet.class );
    }

    @Test
    public void baseGetTest()
    {
        String response = target( "/test" ).request().get( String.class );
        Assert.assertTrue( "92096".equals( response ) );
    }
}

Maven:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>TestWeb</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <!-- Tell Maven what language version to use -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>


    <dependencies>

        <!-- Enables the annotations, etc needed -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.exterprise</groupId>
                    <artifactId>cdi-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Our jersey libs -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>2.25.1</version>
        </dependency>

        <!-- CDI to JAX-RS Binding -->
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers.glassfish/jersey-gf-cdi-ban-custom-hk2-binding -->
        <dependency>
            <groupId>org.glassfish.jersey.containers.glassfish</groupId>
            <artifactId>jersey-gf-cdi</artifactId>
            <version>2.14</version>
        </dependency>

        <!-- TESTING WEB -->
        <dependency>
            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
            <artifactId>jersey-test-framework-provider-jdk-http</artifactId>
            <version>2.25.1</version>
        </dependency>

    </dependencies>

</project>
Don Rhummy
  • 24,730
  • 42
  • 175
  • 330

1 Answers1

1

First you will need to use a container that supports servlet deployment, like the grizzly container

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>${jersey2.version}</version>
</dependency>

Then you need to configure the test class to deploy as a servlet container.

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

@Override
public DeploymentContext configureDeployment() {
    return ServletDeploymentContext.forServlet(TestServlet.class)
            .build();
}

Here, you will not need to override configure method like you're currently doing. However, the above configuration doesn't configure a Jersey application. It only configures your test servlet. If you want to run Jersey in a servlet environment (which the default JerseyTest does not do), then you would configure Jersey like

@Override
public DeploymentContext configureDeployment() {
    final ResouceConfig config = ResourceConfig();
    return ServletDeploymentContext.builder(config)
            .build();
}

There a lot of other things you can configure with the ServletDeploymentContext; just look at the docs.

UPDATE

Instead of trying to start up a server. You might be better off writing a unit test (rather than an integration test), and just mock dependencies using a mocking framework like Mockito. There's an example below. I added some comment as to what each line does. For more info on Mockito, please refer to the linked docs.

public class WebServletTest {

    @Test
    public void testServletGet() throws Exception {
        // create some mocks
        SomeDao dao = mock(SomeDao.class);
        HttpServletRequest request = mock(HttpServletRequest.class);
        HttpServletResponse response = mock(HttpServletResponse.class);
        PrintWriter writer = mock(PrintWriter.class);

        // do some stubbing
        when(response.getWriter()).thenReturn(writer);
        when(dao.getData(anyString())).thenReturn("Hello World");
        // used to capture argument to writer.println
        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);

        // create the servlet and call the method under test
        TestServlet servlet = new TestServlet(dao);
        servlet.doGet(request, response);

        // capture the argument to writer.println
        verify(writer).println(captor.capture());
        String arg = captor.getValue();

        // make assetions
        assertThat(arg).isEqualTo("Hello World");
    }

    private interface SomeDao {
        String getData(String param);
    }

    @WebServlet("/test")
    public static class TestServlet extends HttpServlet {
        private final SomeDao dao;

        @Inject
        public TestServlet(SomeDao dao) {
            this.dao = dao;
        }

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.getWriter().println(this.dao.getData(""));
        }
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • This didn't work for me. Get a 500 error saying it can't connect. `public class TestWebServlet2 extends JerseyTest { @Override public TestContainerFactory getTestContainerFactory() { return new GrizzlyWebTestContainerFactory(); } @Override public DeploymentContext configureDeployment() { return ServletDeploymentContext.forServlet(TestServlet.class) .build(); } @Test public void baseGetTest() { String response = target( "/test" ).request().get( String.class ); Assert.assertTrue( "92096".equals( response ) ); } }` – Don Rhummy Jul 07 '17 at 16:29
  • Worked fine for me when I tested. Is there a stacktrace? If not, try to write a [generic exception mapper](https://stackoverflow.com/a/33684719/2587435) to try to log it – Paul Samsotha Jul 07 '17 at 17:07
  • Ah, it appears doing it this way, it can't do JPA (which is used in my servlet). Error: `javax.servlet.ServletException: org.apache.openjpa.persistence.ArgumentException: The persistence provider is attempting to use properties in the persistence.xml file to resolve the data source. A Java Database Connectivity (JDBC) driver or data source class name must be specified in the openjpa.ConnectionDriverName or javax.persistence.jdbc.driver property. The following properties are available in the configuration` – Don Rhummy Jul 07 '17 at 23:06
  • Just unit test the servlet using mocks. No real need to run a server. You can mock the EntityMananger, and you can mock the HttpServlet(Request|Response). Use a Mockito as your mocking library. – Paul Samsotha Jul 08 '17 at 00:39