Last updated at Wed, 04 Sep 2024 00:28:35 GMT

It’s widely understood that increasing the scope and complexity of a piece of software almost always dramatically increases the effort required to verify it. Verification typically entails testing the behavior of the new feature, plus ensuring no existing functionality has been adversely affected.
Dropwizard

Because this kind of testing can quickly become painful for even the simplest of components, and because at Logentries we continuously deploy large components of our service, anytime we invest in automation rapidly speeds up release cycles and tames the complexity. I’d like to talk briefly about one combination of tools we’ve been trying out recently:

  • Dropwizard– a framework for building ops-friendly microservices
  • Flyway– a tool for managing database schema changes
  • Retrofit– a slick REST client for consuming APIs

Dropwizard does a great job of tying together several top-notch libraries commonly used for developing RESTful web services on the JVM. More importantly, it also packages them up so it’s much easier to do end-to-end testing. Rather than repeating the excellent guide, let’s assume that we have a “Hello, world” service up & running and ready to test (yes, writing tests retrospectively often isn’t advisable, but for the sake of brevity we’re picking up where the guide left off!). We’ll also assume that you have something to persist in a database, so we’ll add one extra method to the HelloWorldResource class, which will return a HTTP 201: CREATED when a request is successful:

@POST 
public Response createSaying(Saying saying);

With that in mind, let’s write some tests. First of all, we’ll create an interface definition for our public API using Retrofit:

package com.example.helloworld.resources;

import retrofit.http.Body;
import retrofit.http.POST;

public interface HelloWorldApi {

 @POST("/hello")
 public Response createSaying(@Body Saying saying);
}

Retrofit can automatically derive an entire API from this interface definition:

RestAdapter adapter = new RestAdapter.Builder()
                                     .setEndpoint("https://api.example.com/")
                                     .build();

// create a client for our Hello World API
HelloWorldApi api = adapter.create(HelloWorldApi.class);

Nice. Now that we have a means of consuming our API, we need to spin up an instance of our application to run some tests. Luckily, Dropwizard provides its own JUunit rules which allow you to boot a standalone container inside a test case as a one-liner (OK, 2 if you count the annotation…):

@ClassRule
public static DropwizardAppRule<HelloWorldConfiguration> rule = new DropwizardAppRule<>(App.class, "test.yml");

That’s all good, but what if your service needs a functioning database to service requests? That’s where Flyway comes in. As well as being a powerful tool for schema versioning, when paired with H2 it gives us a quick and easy way to create test databases on the fly and tear them down when we’re done. To get this working, you’ll first want to add the dropwizard-flyway and h2 dependencies to your POM (apologies if you’re using something other than Maven):

 <dependency>
     <groupId>com.github.joschi</groupId>
     <artifactId>dropwizard-flyway</artifactId>
     <version>0.1.0</version>
 </dependency>
 <dependency>
     <groupId>com.h2database</groupId>
     <artifactId>h2</artifactId>
     <version>1.3.175</version>
 </dependency>

Next, you need to have a YAML file with a test configuration. Here’s an example; the Dropwizard documentation has an extensive reference:

# Database settings.
database:

 # the name of your JDBC driver
 driver_class: org.h2.Driver

 # the username
 user: sa

 # the JDBC URL
 url: jdbc:h2:test

# The rest of your DW config goes here...

Then in your test case, we’ll create create, provision and teardown the database:

@BeforeClass
public static void setupClass() {
    flyway = new Flyway();
    DataSourceFactory f = rule.getConfiguration().getDataSourceFactory();
    flyway.setDataSource(f.getUrl(), f.getUser(), f.getPassword());
    flyway.migrate();
}

// when we're done, destroy the db
@AfterClass
public static void teardownClass() throws IOException {
    Files.deleteIfExists(Paths.get("test.h2.db"));
}

With all that plumbing set up, let’s write a unit test:

@Test
public void testSayingCreation() {
    // ...create a Saying object here, then...
    Response response = api.createSaying(mySaying);
    assertEquals(201, response.getStatus());
}

That’s really all there is to it! This approach has a couple of benefits:

  1. It provides an easy, programmatic way to do high-level testing of a whole service, starting from a clean initial state
  2. It forces you to think about how your APIs look from the outside, and because you’ve just written an interface definition and a client to consume it, you can prepare it for public consumption much faster

This post only scratches the surface of what this toolchain can do, but suffice to say that Dropwizard, Flyway and Retrofit are becoming indispensable tools in our constant drive to improve the speed and quality of our product development.