Getting Started with Dropwizard: TestingPublished on
Some Dropwizard code coverage
The Dropwizard Getting Started guide is a great introduction to the framework, but it in regards to testing the created applications it leaves some wanting. This post will walk one through adding logical and practical tests to the application created in guide and in the end, we will achieve with 100% test coverage.
dropwizard-testing to your
pom.xml. It will pull in common libraries used testing such as JUnit and AssertJ that we’ll use immediately, as well as additional resources for more full stack testing.
Before we get into the web domain, let’s ensure that the endpoint we created in
HelloWorldResource operates as we expect. The source code below should be mostly self-explanatory, as we are dealing with just our class. The annotations such as
Produces, etc, that appear in our resource class do nothing if there is no one to interpret them. There is one potential pitfall, which I’ll go into more detail about after the code.
The one thing of note is the
Before attribute and as the comment explains, its job is to run once before each test to instantiate our resource. We do this to emphasize that internal state is managed by our resource (the counter). We could have omitted the
setup function and used the same resource for all tests like the following:
And all the tests will work together and independently – only because we got lucky. If
idIncrementsByOne executed before
idStartsAtOne, then the check of
isEqualTo(1) would fail because the counter was already incremented by the previous test case.
Now we are confident our resource behaves correctly. Let’s write tests to ensure that clients are receiving the JSON we think we are returning. To achieve this, we will need some help from Dropwizard to setup our class appropriately and host an in-memory server for us to query.
I’ll post the snippet of code and then dive deeper.
This is quite a chunk of code, but we can break it down. First, note the
@Rule. You can imagine that it starts a server with our specified resource for each test case. This means that the resource is recreated for each test case, which is great for us because of the mutable state in our resource. If a resource is immutable, feel free to mark the
@ClassRule and mark the variable as static – you’ll save a few milliseconds per test.
The first test stresses the entire resource soup to nuts. It simulates a client sending a request to
/hello-world?name=dropwizard and we’re saying that the first request will be in the form of:
We then map the raw strings into
Saying objects by using a Dropwizard configured
ObjectMapper that will map the JSON string into the specified class. Finally, we assert that the two
Sayings are equivalent and that they are both equal to the constants specified in the JSON string. The final assert against the constants are important because it serves as a sanity check that deserialization (the process of converting a string into an instantiated class) works as designed.
Alternatively, we could have compared the raw JSON strings directly, but that wouldn’t have tested the deserialization code, and we would have to watch out for whitespace differences. If you want to compare JSON strings directly without worrying about whitespace, formatting, and other options (such as null means the same as nonexistent), take a look at JSONassert and JsonUnit – both are high quality libraries.
The second test case,
helloWorldAbasentName, shows a more terse way of retrieving a
Saying with automatic deserialization and proves that our resource is recreated with each test.
Our tests don’t stress our configuration class (
HelloWorldConfiguration), nor the application class (
HelloWorldApplication). Let’s fix that! Usually, integration testing is not as easy as what will be shown because, as the name implies, it integrates all parts of the application (ie. the database). You can’t mock a database in an integration test! It may seem like there an exorbitant amount of work that goes into integration testing, but the effort is well worth it because then you can be self-assured that all the parts are working cohesively together. After integration testing, it should only be a small step to production.
Here we start a new server for each test by instantiating our
HelloWorldApplication with the
HelloWorldConfiguration that is parsed from the file
my-app-config-yaml. The configuration is loaded from
test\resources\my-app-config.yaml. The server created for the tests is equivalent to:
After running tests and the code coverage, you should see that there is 100% code coverage outside the
main function. Congratulations on achieving a test coverage people dream about!
One caveat, the integration test we created will run whenever our unit tests are ran, which is fine in our case because no external resources are used. In reality, you won’t want to run integration tests with other tests. Splitting tests up is a little outside of scope of this tutorial, but there are satisfactory guides already out there.
As a reference, here is the directory structure that you should have when you finish the tutorial. There are some IDE specific files, such as .idea and dropwizard-example.iml that you can safely ignore.
The final directory structure
The in memory server in the endpoint testing covered our resource’s implementation; however, it is not always the case. When the resource uses a
@Auth in an endpoint, you will need an actual test server that is one-step below an integration test, which the Dropwizard documentation goes in more depth about.