How to Set Up a CI Build for Integration Tests on Jenkins

JAN 09

A Continuous Integration (CI) build with a thorough test suite allows you to be confident that your code changes do not break existing functionality. This helps you avoid introducing bugs which enables you to develop features faster.

A thorough test suite consists of unit tests and integration tests.

Unit tests verify that a small block of code behaves as expected under a well-defined set of external conditions. Unit tests aim to isolate the code under test.

Integration tests exercise the interactions between different components of a system. As such, integration tests often require a testing database. While many software engineers use automated unit tests as part of their standard development practices, they tend to be less consistent about writing automated integration tests.

In this blog post, I will show you how to set up an integration testing environment using a Jenkins CI server and an AWS DynamoDB database. We will set up AWS DynamoDB Local on Jenkins and will populate this database with test data once for the entire test suite. Our tests will be written in Java and compiled with maven.

Start with why

At Twilio, we always say start with why. So before we get started, why do you want to set up a local testing instance of your database? Can we accomplish our testing goals by mocking out the database?

We decided to set up an automated integration testing environment so that we can be confident that the components of our system continue to communicate correctly with each commit. We use an instance of AWS DynamoDB deployed on a Jenkins CI server so that our testing conditions closely reflect our production environment.

Code Layout


Installing a local instance of AWS DynamoDB on your Jenkins CI server

Download AWS DynamoDB and place all jars and licenses in a folder according to the layout above.

Choosing a database port

The following script creates a new socket and returns the socket’s address.
This address is the port we assign to AWS DynamoDB. See the socket module documentation for more information.

We run the script and assign the output to an environment variable DYNAMODB_PORT.


#!/usr/bin/env python import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 0))
addr = s.getsockname() print addr[1] s.close() 

Start and Stop an AWS DynamoDB Instance on the chosen database port

The tests and require an instance of AWS DynamoDB to be running in order to pass. In this section, I start an AWS DynamoDB server, build my project, which runs my tests, and then stop the AWS DynamoDB server. My configuration places the following commands in a yaml file.

To Start AWS DynamoDB: run ./ $DYNAMODB_PORT

    set -x

    pushd DynamoDBLocal 
    java -mx512m -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -port $PORT 2>&1  &> dynamo.log popd echo $PORT 

Now I can build my project with this maven command.
mvn -Daws.accessKeyId=aws_key -Daws.secretKey=aws_secret clean package

And once the build completes, stop the AWS DynamoDB instance: ./ $DYNAMODB_PORT

    set -e set -x

    PORT=$1 kill -int `netstat -nap | grep LISTEN | grep :$PORT | awk {'print $7'} | sed 's/\/.*//'` 

set -e forces the bash script to fail immediately when any command returns an non-zero exit code.

set -x prints commands and their arguments as they are executed in the script.

kill -int kills the running process and allows java to clean up resources.

netstat -nap | grep LISTEN | grep :$PORT displays the process that is listening on $PORT.

Then awk {'print $7'} | sed 's/\/.*//' parses the full process entry to get the process id.

For more information, go to the netstat documentation.

Configure AWS DynamoDB in your java project Remember that we set the

DYNAMODB_PORT environment variable above. This allows us to dynamically set and use a port available on our Jenkins server. We read in this value in the java configuration using System.getenv("DYNAMODB_PORT"):
public class TwilioDynamoDBConfiguration {

        public int port = Optional.ofNullable(System.getenv("DYNAMODB_PORT")).map(Integer::parseInt).orElse(443);

Populate your AWS DynamoDB database with test data

Now that we have the correct AWS DynamoDB port, we can initialize the AWS DynamoDB database. The first test has the following setUp() method where initializer.setup() is responsible for populating the AWS DynamoDB database with test data. We place the @BeforeClass annotation before the first test in the test suite so that we only need to initialize the data for AWS DynamoDB once. We choose @BeforeClass instead of @Before because @BeforeClass runs only once, independendent of the number of @Test methods in a class, whereas@Before runs once before each test in a class. We only populate our database once because this operation is expensive.
import org.junit.BeforeClass;

public class DynamoDbTest { 
    public static void initializeDynamo() { 
        DynamoDBTestInitializer initializer = DynamoDBTestInitializer.getInstance(); 


    public static void additionalDynamoInitialization() { } 
import org.junit.Test;

public class MyFirstDynamoDbTest extends DynamoDbTest { 

    public void testCanViewItemA() { 

      Response response = httpGet("A");
      String body = response.readEntity(String.class); 
      assertThat(body.contains("Item A"));

    public void testCanViewItemB(){ ...  } 
import org.junit.Test;

class MyMoreComplicatedTest extends DynamoDbTest {

   public static void additionalDynamoInitialization() {
     // Add some more fixtures to dynamo

   public void testSomethingComplex() {
      // use the fixtures in your assertions
public class DynamoDBTestInitializer{

    public static void setup(){ 

    * the call to httpPost writes data to the database through our
    * application's API
    private static void createItemA(){ 
      Form form = new Form();
      form.param("Item A", "Item of type A"); 
      form.param("Special Description", "More special details on item A");

      Response response = httpPost("A", form);

    private static void createItemB(){ 
      Form form = new Form();
      form.param("Item B", "Item of type B"); 
      form.param("Description", "More details on item B");

      Response response = httpPost("B", form);

We have setup an automated integration testing environment with AWS DynamoDB and Jenkins. Now we can write integration tests which give us confidence that the components of our system interact correctly, and will continue to do so as we write new code.

Image of Coverage Report

Posted by Cara Borenstein on January 09, 2017