Testing with the Jersey Test Framework

Testing

Unit Tests

deal with atomic units of work, i.e.

  • minimum units of work
  • without any dependencies

Integration Tests

deal with logical units of works

  • composed of multiple sub units
  • that may be interdependent

Have an example

Hello Jersey

@Path("rest/hellojersey")
public class HelloJersey {
    @GET
    @Path("hello")
    @Produces(MediaType.APPLICATION_JSON)
    public Greeting getGreeting() {
        return greetingDatabase.defaultGreeting();
    }
    @GET
    @Path("echo/{echo}")
    public Greeting getEcho(
        @PathParam("echo") String echo) {
        return new Greeting(new StringBuilder(echo)
            .reverse().toString());
    }
    @GET
    @Path("hello/store/{alias}")
    @Produces(MediaType.APPLICATION_JSON)
    public Greeting retrieveGreeting(
        @PathParam("alias") String alias) {
        return greetingStore.findGreeting(alias);
    }
    @POST
    @Path("hello/store/{alias}")
    @Consumes(MediaType.APPLICATION_JSON)
    public void storeGreeting(
        @PathParam("alias") String alias, Greeting greeting) {
        greetingDatabase.storeGreeting(alias, greeting);
    }

Hello Jersey API

{
    "apiVersion":"1.0.0",
    "swaggerVersion":"1.2",
    "apis":
        [
            {
                "path":"/hellojersey",
                "description":"'Hello World'-style endpoint"
            }
        ]
}

Testing strategies

  • JUnit unit tests
  • RESTassured
  • Jersey Test Framework

JUnit

Setup

    @Mock
    GreetingDatabase greetingDatabase;

    @InjectMocks
    HelloJersey helloJersey;

    @Before
    public void setUp() {
        helloJersey = new HelloJersey();
        MockitoAnnotations.initMocks(this);

        Mockito.when(greetingDatabase
            .findGreeting(Mockito.anyString()))
                .thenAnswer(invocation -> {
            String name = (String) invocation.getArguments()[0];

            if (name.equals("hawaii")) {
                return new Greeting("Aloha!");
            } else {
                return new Greeting("Hello, I am a mock response.");
            }
        });
        Mockito.when(greetingDatabase.defaultGreeting())
            .thenAnswer(invocation -> new Greeting("Hello mock Jersey."));
    @Test
    public void testHello() {
        assertEquals(new Greeting("Hello mock Jersey.").
            getMessage(), helloJersey.getGreeting().getMessage());
    }
    @Test
    public void testHelloEcho() {
        assertEquals(new Greeting(new StringBuilder(
            "Hello, I am a mock response.").reverse().toString())
                        .getMessage(),
                helloJersey.getEcho("Hello, I am a mock response.")
                    .getMessage());
    }
    @Test
    public void testRetrieveStoredGreeting() {
        assertEquals(new Greeting(
            "Hello, I am a mock response.").getMessage(), 
                helloJersey.retrieveGreeting
                    ("anything").getMessage());
    }
    @Test
    public void testStoreGreeting() {
        Greeting greeting = new Greeting("Aloha!");
        helloJersey.storeGreeting("hawaii", greeting);
        assertEquals(helloJersey.retrieveGreeting("hawaii").
            getMessage(), greeting.getMessage());
    }

RESTassured

Setup

    static boolean connectionExists = false;

    @BeforeClass
    public static void setUp() {
        RestAssured.baseURI = "http://localhost";
        RestAssured.port = 8080;
        RestAssured.basePath = "/app";

        try {
            Response response = expect().statusCode(200)
                .contentType(ContentType.JSON).when()
                    .get("/rest/hellojersey/hello");

            String responseString = 
                new JsonPath(response.getBody().asString())
                    .getString("message");
            connectionExists =
                "Hello Jersey!".equals(responseString);
        } catch (Exception e) {
        }
    }
    @Test
    public void testHello() {
        assumeTrue(connectionExists);
        Response response = expect().statusCode(200)
            .contentType(ContentType.JSON).when()
                .get("/rest/hellojersey/hello");

        assertEquals("Hello Jersey!", 
            new JsonPath(response.getBody().asString())
                .getString("message"));
    }
    @Test
    public void testHelloEcho() {
        assumeTrue(connectionExists);
        Response response = given().pathParam("echo", "echo")
            .expect().statusCode(200).when()
                .get("/rest/hellojersey/echo/{echo}");

        assertEquals("ohce", 
            new JsonPath(response.getBody().asString())
                .getString("message"));
    }

Jersey Test Framework

Setup

public class HelloJerseyJerseyTest extends JerseyTest {

    @Mock
    GreetingDatabase greetingDatabase;


    @Override
    protected Application configure() {
        MockitoAnnotations.initMocks(this);

        Mockito.when(greetingDatabase
            .findGreeting(Mockito.anyString()))
                .thenAnswer(invocation -> {
                    String name = 
                        (String) invocation.getArguments()[0];

            if (name.equals("hawaii")) {
                return new Greeting("Aloha!");
            } else {
                return new Greeting("Hello, I am a mock response.");
            }
        });
        Mockito.when(greetingDatabase.defaultGreeting())
            .thenAnswer(invocation -> new Greeting("Hello mock Jersey!"));
        Mockito.doNothing().when(greetingDatabase)
            .storeGreeting(Mockito.anyString(), Mockito.any(Greeting.class));
...

Setup (continued)

...

        // context injection will not work
        //thus specify injection behaviour explicitly
        resourceConfig.register(new AbstractBinder() {
            @Override
            protected void configure() {
                bind(greetingDatabase).to(GreetingDatabase.class);
            }
        });
        ResourceConfig resourceConfig = 
            new ResourceConfig(HelloJersey.class);
        return resourceConfig;
    }
    @Test
    public void testHelloObject() {
        final String hello = target("rest/hellojersey/hello").
            request().get(Greeting.class).getMessage();
        assertEquals("Hello Jersey!", hello);
    }
    @Test
    public void testHelloJSON() {
        final String hello = 
            target("rest/hellojersey/hello")
                .request().get().readEntity(String.class);
        //overkill at this point, but a simple string 
        //comparison does not suffice for JSON
        // - contents may be in any order
        JsonParser parser = new JsonParser();
        assertEquals(parser.parse(new Gson()
            .toJson(new Greeting("Hello mock Jersey!"))), 
                parser.parse(hello));
    }
    @Test
    public void testStoreGreeting() {
        Greeting greeting = new Greeting("");
        Entity<Greeting> entity = 
            Entity.entity(greeting, MediaType.APPLICATION_JSON_TYPE);

        assertEquals(204, 
            target("rest/hellojersey/hello/store/newgreeting")
                .request().post(entity).getStatus());
    }

Conclusions

The Good

  • fast (in memory container)
  • very few dependencies (in memory container)
  • proper REST endpoints

The Bad

  • slow (external container)
  • environmental dependencies (external container)
  • no proper container environment (in memory)

The Ugly

  • mocking requires context/dependency injection
  • still no proper unit test

Miscanelleous

  • slides.com/manuelweidmann/jerseytesting
  • github.com/vyo/hellojersey
Made with Slides.com