users@jersey.java.net

Here is what I want to do (TDD testing with Spring)

From: Mats Henricson <Mats.Henricson_at_paf.com>
Date: Tue, 24 Mar 2009 12:58:57 +0200

OK, here is what I want to do as a TDD developer. Sorry for
such a long email. I've tried to simplify my code as much as
possible for this post, and may have missed in cases.

First lets assume I have a class used inside a Jersey resource
for its implementation:

package foo;

@Component
public class BarImpl implements Bar {
    public String getHello(long id) throws BarException {
        
        if (id == -1) {
            throw new BarException("No such user: " + id);
        }
                
        return "Hello " + id;
    }
}

That BarException is in package foo.exceptions and totally trivial.
The Bar interface is also totally trivial.

Now this is my resource class:

package foo.resource;

@Path("/hello")
@Component
public class BarResource {

    @Autowired
    private Bar bar;

    @GET
    @Produces("text/plain")
    public String getHello(@QueryParam("id") long id) throws BarException {
        return bar.getHello(id);
    }

    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

Now, lets test this baby with JUnit. I write an applicationContext-test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans LOTS OF XML MUMBO JUMBO>

  <bean id="bar" class="foo.BarImpl">
  </bean>

  <bean id="barResource" class="foo.resource.BarResource">
    <property name="bar" ref="bar"/>
  </bean>
</beans>

The JUnit test of the resource class is a beauty of simplicity:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext-test.xml" })
public class BarResourceTest {

    @Autowired
    private BarResource barResource;

    @Test
    public void testWithBogusId() {
            try {
            /* IGNORE RETURNED VALUE */ barResource.getHello(-1);
            Assert.fail();
            }
            catch (BarException e) {
                String message = e.getMessage();
            Assert.assertEquals(message, "No such user: -1");
            }
    }
    
    @Test
    public void testWithOkId() throws BarException {
            String message = barResource.getHello(77);
            Assert.assertEquals(message, "Hello 77");
    }
}

Now, how do I test this resource over HTTP? A complicating issue is that
I have an exception mapper:

@Provider
public class BarExceptionMapper implements ExceptionMapper<BarException> {

    public Response toResponse(BarException e) {
        return Response.status(Response.Status.NOT_FOUND).
               entity(e.toString()).type("text/plain").build();
    }
}

My (probably totally naive) attempt was this, from looking all over the
net for examples:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext-test.xml" })
public class BarResourceRestTest {

    private static final String baseUri = "http://localhost:9998/";
    private static final String resourcePackage =
                                BarResource.class.getPackage().getName();
    private static final String exceptionMapperPackage =
                                BarExceptionMapper.class.getPackage().getName();
    
    private SelectorThread threadSelector;
    
    @Before
    public void setup() throws IllegalArgumentException, IOException {
        final Map<String, String> initParams = new HashMap<String, String>();

        initParams.put("com.sun.jersey.config.property.packages",
                       resourcePackage + ";" + exceptionMapperPackage);
        initParams.put("com.sun.jersey.config.property.resourceConfigClass",
                       "com.sun.jersey.api.core.PackagesResourceConfig");

        threadSelector = GrizzlyWebContainerFactory.create(baseUri, initParams);
    }
    
    @After
    public void shutDown() {
        threadSelector.stopEndpoint();
    }
    
    @Test
    public void testWithBogusId() throws IOException {
        URL url = new URL(baseUri + "hello/-1");
        String data = getDataFromUrl(url);
        
        Assert.assertTrue(data.equals("No such user: -1"));
    }

    @Test
    public void testWithOkId() throws IOException {
        URL url = new URL(baseUri + "hello/77");
        String data = getDataFromUrl(url);
        
        Assert.assertTrue(data.equals("Hello 77"));
    }
    
    private String getDataFromUrl(URL url) throws IOException {
        // Read all the text returned by the server
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(url.openStream()));
        StringBuffer data = new StringBuffer(1000);
        String line;

        while ((line = in.readLine()) != null) {
            data.append(line);
        }

        in.close();
        
        return data.toString();
    }
}

I can't get this to work. I've fiddled with various things, and I either
get a NullPointer where the bar object is used in this function:

    public String getHello(@QueryParam("id") long id) throws BarException {
        return bar.getHello(id);
    }

Or, I get an exception, because the ExceptionMapper isn't in place.

I clearly don't want to fiddle with web.xml. I just want to deploy my
resource in Grizzly (or whatever standalone container) and make an HTTP
call. I would also prefer not to embed GlassFish in my JUnit tests.
We use WebLogic, but not in our JUnit tests, of course.

Mats