Over a million developers have joined DZone.

DynamoDB + Rake + Maven + Rack

DZone's Guide to

DynamoDB + Rake + Maven + Rack

Learn to create integration tests for a DynamoDB cloud database in a Ruby app using Rake, Apache Maven, and Rack with this tutorial.

· Integration Zone ·
Free Resource

WSO2 is the only open source vendor to be named a leader in The Forrester Wave™: API Management Solutions, Q4 2018 Report. Download the report now or try out our product for free.

In SixNines.io, one of my Ruby pet web apps, I'm using DynamoDB, a NoSQL cloud database by AWS. It works like a charm, but the problem is that it's not so easy to create an integration test, to make sure my code works together with the "real" DynamoDB server and tables. Let me show you how it was solved. The code is open source and you can see it in the yegor256/sixnines GitHub repo.

How to Bootstrap DynamoDB Local

First, you need to use DynamoDB Local, a command line tool created by AWS exactly for the purposes of testing. You need to start it before your integration tests and stop it afterward.

To make things simpler, I suggest you use jcabi-dynamodb-maven-plugin, a Maven plugin. You will need to add pom.xml to your repository and start/stop Maven from a Rakefile, just like I'm doing here:

task: dynamo do FileUtils.rm_rf('dynamodb-local/target') pid = Process.spawn('mvn', 'install', chdir: 'dynamodb-local') at_exit do `kill -TERM #{pid}`
  begin Dynamo.new.aws.describe_table(table_name: 'sn-endpoints') rescue Exception => e sleep(5) retry end end

First, I'm removing dynamodb-local/target, the directory where Maven keeps its temporary files, to make sure we always start from scratch.

Then, I'm starting mvn install, using Process.spawn as a background process with pid as its process ID (this won't work in Windows, only Linux/Mac). Then I immediately register an at_exit Ruby hook, which will be executed if Ruby dies for any reason. I'm sure it's obvious why I have to do that—in order to avoid garbage running in the background after Rake is finished or terminated.

Pay attention, I'm using kill -TERM instead of kill -KILL, in order to give Maven a chance to wrap everything up, terminate DynamoDB Local correctly, close its TCP port, and exit.

How to Check That It's Running

Next, I'm checking the status of sn-endpoints, one of the tables in the DynamoDB Local. It has to be there if the server is up and running. It will be created by jcabi-dynamodb-maven-plugin according to sn-endpoints.json, its JSON configuration.

Most probably, the table won't be ready immediately, though, since it takes time to bootstrap Maven, start the server, and create tables there. That's why, if the exception is thrown, I catch it, wait for 5 seconds, and try again. I keep trying many times until the server is ready. Eventually, it will be. It takes about 12-15 seconds on my MacBook, which means 2-3 attempts/exceptions.

How to Connect to DynamoDB Local

My classes need to know how to connect to the server during integration tests. In production, they need to connect to AWS, in testing, they have to know about the DynamoDB Local instance I just started. This is what I have in my class Dynamo, which is responsible for the very connection with DynamoDB. Its decision on where to connect is based on the environment variable RACK_ENV, which is set to "test" in test__helper.rb, which is included by rake/testtask in front of all other tests, thanks to the double underscore in its name.

If the environment variable is set to "test", Dynamo takes the connectivity parameters from the YAML file dynamodb-local/target/dynamo.yml created by maven-resources-plugin:copy-resources. The TCP port of the DynamoDB Local database will be there, as well as the DynamoDB authentication key and secret.

How to Run the Integration Tests

This is the easiest part. I just use my objects the way they are supposed to be used in production and they connect to DynamoDB Local instead of AWS.

I'm using Rack::Test in order to test the entire application, via a set of HTTP calls. For example, here I'm trying to render Jeff's user account page. Its HTTP response code is supposed to be 200:

require 'test/unit'
require 'rack/test'
class AppTest < Test::Unit::TestCase include Rack::Test::Methods def app Sinatra::Application end def test_user_account header('Cookie', 'sixnines=jeff') get('/a') assert_equal(200, last_response.status) end

Now you can run the entire test from the command line. You can see how runs it while releasing a new version: full log. Also, see how it works in Travis. In a nutshell:

  • You call rake in the command line and Rake starts;
  • Rake attempts to run the default task, which depends on test;
  • Rake attempts to run test, which depends on dynamo;
  • Rake, inside test task, runs mvn install in the background with this pom.xml;
  • Maven unpacks DynamoDB Local installation package;
  • Maven reserves a random TCP port and stores its value into ${dynamo.port};
  • Maven saves ${dynamo.port} and key/secret pair info;
  • Maven starts DynamoDB Local, binding it to the reserved TCP port;
  • Rake waits for DynamoDB Local availability on the reserved port;
  • Rake imports all test classes starting from test__helper.rb;
  • Environment variable RACK_ENV is set to "test";
  • Rack::Test attempts to dispatch a web page;
  • Dynamo loads YAML config from dynamo.yml and connects to DynamoDB Local;
  • Rake stops;
  • Ruby terminates Maven and it stops DynamoDB Local.

That's it.

IAM is now more than a security project. It’s an enabler for an integration agile enterprise. If you’re currently evaluating an identity solution or exploring IAM, join this webinar.

integration ,tutorial ,dynamodb ,rake ,maven ,rack ,integration testing ,testing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}