fei
fei

Reputation: 2384

unit test jersey rest call with jersey test framework

I'm writing unit test for my rest call (implemented in jersey) with the jersey test framework, I'm getting an IncompatibleClassChangeError, and it's really confusing:

    Caused by: java.lang.IncompatibleClassChangeError: Class javax.ws.rs.core.Response$Status does not implement the requested interface javax.ws.rs.core.Response$StatusType
 at com.sun.jersey.spi.container.ContainerResponse.getStatus(ContainerResponse.java:548)
 at com.sun.jersey.spi.container.ContainerResponse$CommittingOutputStream.commitWrite(ContainerResponse.java:156)
 at com.sun.jersey.spi.container.ContainerResponse$CommittingOutputStream.write(ContainerResponse.java:133)
 at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
 at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
 at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
 at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
 at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
 at java.io.BufferedWriter.flush(BufferedWriter.java:236)
 at com.sun.jersey.core.util.ReaderWriter.writeToAsString(ReaderWriter.java:191)
 at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:128)
 at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:88)
 at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:58)
 at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:299)
 at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1326)
 at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1239)
 at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1229)
 at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:420)
 at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:497)
 at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:684)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
 at com.sun.grizzly.http.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:188)
 ... 20 more

My rest call is like this:

@GET
@Produces("application/json")
@Path("/test") 
public String test() {
    return "it works";
}

My test is like this:

public class MyTest extends JerseyTest {

    public MyTest() {
        super("com.mypackage");
    }

    @Test
    public void test() throws IllegalArgumentException, IOException {
        WebResource webResource = resource();
        webResource.path("/test").accept("application/json").get(ClientResponse.class).toString();
    }
}

The 2nd line is throwing the exception. So what am I doing wrong here? I'm using jersey 1.4 and also 1.4 for the jersey test framework. Any help would be appreciated. Thanks!

Update: I notice the test will pass if I run it in command line via maven, weird.

Upvotes: 0

Views: 4672

Answers (3)

guilhebl
guilhebl

Reputation: 8990

Try to change the setup of your Jersey Test to override configure instead:

    public class MyTest extends JerseyTest {

    @Override
    protected Application configure() {
        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);

        MyResource service = new MyResource();
        ResourceConfig config = new ResourceConfig();
        config.register(service);        

        Map<String, Object> mapProps = new HashMap<>();      mapProps.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE,Boolean.TRUE);
        config.addProperties(mapProps);

        return config;
    }

    /**
     * expects HTTP 200 for valid request
     * @param l
     * @param expectedText
     */
    private void expectValidRequest(String expectedText) {     
        Response output = target("/test").request(MediaType.APPLICATION_JSON).get();

        Assert.assertTrue(output.getStatus() == HttpStatus.OK_200);             
        Assert.assertTrue(output.getHeaders().get("Content-Type").get(0).equals(MediaType.APPLICATION_JSON));
        String textBody = output.readEntity(String.class);              
        Assert.assertTrue(textBody.equals(expectedText));   
    }

    @Test
    public void test() throws IllegalArgumentException, IOException {
        String expectedText = "{\"name\": \"it works\"}";
        expectValidRequest(l, expectedText);  
    }
}

// your Client response class - sample
    class ClientResponse {
        public ClientResponse(String name) {
            super();
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }


// your web service
@GET
@Produces("application/json")
@Path("/test") 
public ClientResponse test() {
    return new ClientResponse("it works");
}

Upvotes: 0

Luis Ramirez-Monterosa
Luis Ramirez-Monterosa

Reputation: 2242

your method returns a string

@GET
@Produces("application/json")
@Path("/test") 
public String test() {
    return "it works";
}

so you should be expecting a String.class

public class MyTest extends JerseyTest {

    public MyTest() {
        super("com.mypackage");
    }

    @Test
    public void test() throws IllegalArgumentException, IOException {
        WebResource webResource = resource();
        assertEquals("it works",webResource.path("/test").accept("application/json").get(String.class));
    }
}

Upvotes: 0

bcarlso
bcarlso

Reputation: 2345

Not really an answer to your question, but I have to ask it anyway. I'm sure you've simplified the example, but I don't really see much value in writing a unit test for this. It seems as if you're testing the framework more than any real application logic. If your get request invokes some rules to return some sort of model objects, simply wrap that code in an object and use DI to inject it.

For example:

@GET
@Produces("application/json")
@Path("/movies") 
public List<Movie> catalog() {
    return this.rentalManager.findAll();
}

Now you can just use a boring old JUnit test and not deal with any of the framework issues. Is it testing that the proper annotations are present on your class? Not really, but I suspect that it's something that will be caught in acceptance testing and doesn't change much once it's coded.

Of course if it really bothered you then you could certainly write some kind of test like:

@Test
public void catalogSupportsGetRequests() {
     verifyMethodHasAnnotation(MyResource.class, "catalog", "GET");
}

@Test
public void catalogProducesJson() {
     verifyMethodHasAnnotation(MyResource.class, "catalog", "Produces", "application/json");
}

// You get the idea.

Hope that helps! Brandon

Upvotes: 0

Related Questions