Testing a Dropwizard Application - RESTful Web Services with Dropwizard (2014)

RESTful Web Services with Dropwizard (2014)

Appendix A. Testing a Dropwizard Application

Our application is ready. However, if we respect its stability, we have to make sure that we at least have its most important aspects covered by unit tests. You are probably familiar with unit testing and JUnit, but Dropwizard takes this a little bit further.

The dropwizard-testing module includes everything you need, such as JUnit and FEST assertions, in order to create tests for your application, right from small unit tests to bigger, full-fledged tests.

Creating a complete test for the application

Let's create a complete, fully automated integration test for our application. This test should start our application as we would normally do for a manual test, and perform some HTTP requests to the application's services which check how the application is responding.

Getting ready

When we first created our project using Maven in Chapter 2, Creating a Dropwizard Application, a JUnit dependency had been automatically added in our pom.xml file. We will replace it with Dropwizard's testing module, so let's remove it. Locate and delete the following dependency from the pom.xml file:

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>3.8.1</version>

<scope>test</scope>

</dependency>

We will need the dropwizard-testing and hamcrest-all modules, so include them both in your pom.xml file:

<dependency><groupId>io.dropwizard</groupId><artifactId>dropwizard-testing</artifactId><version>0.7.0-SNAPSHOT</version></dependency><dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-all</artifactId><version>1.3</version></dependency>

How to do it…

Your project already has a test folder. During the generation of the default artifact, Maven created both src/main/java (where our application's source code lies) and src/test/java as a placeholder for our unit tests. Let's see what we need to place there in order to build our tests:

1. Create a new test class, ApplicationTest, within the src/test/java/com/dwbook/phonebook folder, extending the ResourceTest base class. This class needs to have two methods; #setUp(), in which we will prepare our mocked objects and add the required resources and providers to the memory inJersey server, and #createAndRetrieveContact(), where we will perform the actual test:

2. package com.dwbook.phonebook;

3.

4. import static org.fest.assertions.api.Assertions.assertThat;

5.

6. import javax.ws.rs.core.MediaType;

7.

8. import org.junit.Before;

9. import org.junit.ClassRule;

10.import org.junit.Test;

11.import com.dwbook.phonebook.representations.Contact;

12.import com.sun.jersey.api.client.Client;

13.import com.sun.jersey.api.client.ClientResponse;

14.import com.sun.jersey.api.client.WebResource;

15.import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

16.

17.import io.dropwizard.testing.junit.DropwizardAppRule;

18.

19.public class ApplicationTest {

20.

21. private Client client;

22.

23. private Contact contactForTest = new Contact(0, "Jane", "Doe", "+987654321");

24.

25. @ClassRule

26. public static final DropwizardAppRule<PhonebookConfiguration> RULE =

27. new DropwizardAppRule<PhonebookConfiguration>(App.class, "config.yaml");

28.

29. @Before

30. public void setUp() {

31. client = new Client();

32. // Set the credentials to be used by the client

33. client.addFilter(new HTTPBasicAuthFilter("wsuser", "wsp1"));

34. }

35.

36. @Test

37. public void createAndRetrieveContact() {

38. // Create a new contact by performing the appropriate http request (POST)

39. WebResource contactResource = client.resource("http://localhost:8080/contact");

40. ClientResponse response = contactResource

41. .type(MediaType.APPLICATION_JSON)

42. .post(ClientResponse.class, contactForTest);

43. // Check that the response has the appropriate response code (201)

44. assertThat(response.getStatus()).isEqualTo(201);

45.

46. // Retrieve the newly created contact

47. String newContactURL = response.getHeaders().get("Location").get(0);

48. WebResource newContactResource = client.resource(newContactURL);

49. Contact contact = newContactResource.get(Contact.class);

50. // Check that it has the same properties as the initial one

51. assertThat(contact.getFirstName()).isEqualTo(contactForTest.getFirstName());

52. assertThat(contact.getLastName()).isEqualTo(contactForTest.getLastName());

53. assertThat(contact.getPhone()).isEqualTo(contactForTest.getPhone());

54. }

}

55. Our tests will run every time we issue the mvn package command, but they can also be executed on demand with the test command of mvn. For now, let's run the test on a clean application environment by issuing the following command:

56.$ mvn clean test

You will see that Maven will clean our target directory, start the application, and then run our tests successfully.

How to do it…

How it works…

Firstly, we defined our test data; that is, a Contact instance that we intend to create.

We initialized a DropwizardAppRule<PhonebookConfiguration> instance, which is described as a JUnit rule for starting and stopping your application at the start and end of a test class, allowing the test framework to start the application as you would normally do in order to perform a manual test. For this, we need to specify not only the main class of our application, but also the configuration file to be used.

Within the #setUp() method, we instantiated a REST client to help us with the HTTP requests to our application and also applied the necessary HTTP basic authentication filter since our web services require authentication.

The #createAndRetrieveContact() method wraps the actual test. Using the REST client, we are performing an HTTP POST request in order to create a new contact. After such a request, we expect an HTTP response with the code 201 – Created response. We test whether the response code is the one we expected with the assertThat() and isEqual() helper methods, which are provided by the Fixtures for Easy Software Testing (FEST) libraries. As stated on the home page of the FEST project(http://code.google.com/p/fest/):

"FEST is a collection of libraries, released under the Apache 2.0 license, whose mission is to simplify software testing. It is composed of various modules, which can be used with TestNG or JUnit."

There's more…

We just showcased the use of the Dropwizard testing module in order to perform an integration test by booting an actual server that is connected to an actual database. This module is not limited to integration testing though. It is backed by JUnit, and you are able to use it for smaller (but critical) to larger unit tests and also for testing the correct serialization/deserialization of entities.

Adding health checks

A health check is a runtime test for our application. We are going to create a health check that tests the creation of new contacts using the Jersey client.

The health check results are accessible through the admin port of our application, which by default is 8081.

How to do it…

To add a health check perform the following steps:

1. Create a new package called com.dwbook.phonebook.health and a class named NewContactHealthCheck in it:

2. import javax.ws.rs.core.MediaType;

3. import com.codahale.metrics.health.HealthCheck;

4. import com.dwbook.phonebook.representations.Contact;

5. import com.sun.jersey.api.client.*;

6.

7. public class NewContactHealthCheck extends HealthCheck {

8. private final Client client;

9.

10. public NewContactHealthCheck(Client client) {

11. super();

12. this.client = client;

13. }

14.

15. @Override

16. protected Result check() throws Exception {

17. WebResource contactResource = client

18. .resource("http://localhost:8080/contact");

19. ClientResponse response = contactResource.type(

20. MediaType.APPLICATION_JSON).post(

21. ClientResponse.class,

22. new Contact(0, "Health Check First Name",

23. "Health Check Last Name", "00000000"));

24. if (response.getStatus() == 201) {

25. return Result.healthy();

26. } else {

27. return Result.unhealthy("New Contact cannot be created!");

28. }

29. }

}

30. Register the health check with the Dropwizard environment by using the HealthCheckRegistry#register() method within the #run() method of the App class. You will first need to import com.dwbook.phonebook.health.NewContactHealthCheck. The HealthCheckRegistrycan be accessed using the Environment#healthChecks() method:

31. // Add health checks

e.healthChecks().register("New Contact health check", new NewContactHealthCheck(client));

32. After building and starting your application, navigate with your browser to http://localhost:8081/healthcheck:

How to do it…

The results of the defined health checks are presented in the JSON format. In case the custom health check we just created or any other health check fails, it will be flagged as "healthy": false, letting you know that your application faces runtime problems.

How it works…

We used exactly the same code used by our client class in order to create a health check; that is, a runtime test that confirms that the new contacts can be created by performing HTTP POST requests to the appropriate endpoint of the ContactResource class. This health check gives us the required confidence that our web service is functional.

All we need for the creation of a health check is a class that extends HealthCheck and implements the #check() method. In the class's constructor, we call the parent class's constructor specifying the name of our check—the one that will be used to identify our health check.

In the #check() method, we literally implement a check. We check that everything is as it should be. If so, we return Result.healthy(), else we return Result.unhealthy(), indicating that something is going wrong.