This is a re-post of my article over at Kabisa’s The Guild.
I’ve been busy rewriting Firefly for a while now using Hanami. Hanami is a fascinatingly fresh ruby web framework with a strong opinion on Clean Architecture. Me like!
Why test against different databases#
Initially I developed Firefly to use Sqlite for database storage. However, Sqlite is not always the best option. Running Firefly on Heroku for instance would be impractical, since Heroku’s architecture assumes you use a real database, like Postgres.
Firefly being open source also means that different users will want to use a database that they’re already familiar with, or one that’s already running for other apps.
Supporting multiple databases#
The big three relational databases I want to support are Sqlite, MySQL and PostgreSQL. Hanami uses Sequel. This means my code is already abstracted from specific database implementation. As long as Sequel supports it, so can Firefly.
The only problem I encountered was the fact that MySQL datetime
fields do not store
fractions of seconds, which messed with some tests. This was easily taken care of.
Travis#
Travis is an awesome CI-as-a-Service provider. Open source projects can even use their service for free! <3
Whenever a new commit is made (on master
or in Pull Requests), Travis will
check out the code, do some setup specified in a .travis.yml
file and report
back the test status.
The thing is that, with multiple databases, we need to tell Travis to run multiple sub-builds for each database. We also need to tell Travis how to configure / setup Firefly to use each database properly.
Hanami and databases#
Before setting up multiple databases, let’s check how Hanami configures a database connection.
# lib/firefly.rb
Hanami::Model.configure do
# * SQL adapter
# adapter type: :sql, uri: 'sqlite://db/firefly_development.sqlite3'
# adapter type: :sql, uri: 'postgres://localhost/firefly_development'
# adapter type: :sql, uri: 'mysql://localhost/firefly_development'
#
adapter type: :sql, uri: ENV['FIREFLY_DATABASE_URL']
So, the actual database URI is set using an environment variable. Hanami
makes use of the dotenv
gem, which will load a .env
or .env.test
file
depending on which environment Hanami runs in. Somehow we’d need different
.env.test
files for each database configuration
For Sqlite:
# .env.test.sqlite
FIREFLY_DATABASE_URL="sqlite://db/firefly_development.sqlite3"
For MySQL:
# .env.test.mysql
FIREFLY_DATABASE_URL="mysql://root@localhost/firefly_test"
For Postgres:
# .env.test.postgresql
FIREFLY_DATABASE_URL="postgres://localhost/firefly_test"
There’s also the issue of dependencies. For instance, when using PostgreSQL,
the pg
gem should be included in Gemfile
. If you’re running with Sqlite,
you do not want that dependency there. Here I’d like to take the same
approach and create multiple Gemfiles that each specify their own
dependencies as needed.
For Sqlite:
# gemfiles/Gemfile.sqlite
gem 'sqlite3'
For MySQL:
# gemfiles/Gemfile.mysql
gem 'mysql'
For Postgres:
# gemfiles/Gemfile.postgresql
gem 'pg'
Tying it all together#
What’s left to is tell Travis about the different database and put the right files in place at the right time.
First, I setup the environment variables for each database. This triggers Travis to run a build for each combination of variables:
# .travis.yml
env:
- DB=sqlite
- DB=mysql
- DB=postgresql
Travis will run the build three times, each time with a different DB
value set.
The process of the Firefly build is like this:
# .travis.yml
install: bundle install --jobs=3 --retry=3 --without production
script:
- 'HANAMI_ENV=test bundle exec hanami db create'
- 'HANAMI_ENV=test bundle exec hanami db migrate'
- 'bundle exec rake test'
What I want is hook into different places and setup the right .env
and Gemfile
for the specified database. As it turns out Travis provides before_install
and
before_script
hooks. By making use of the specified DB
environment variable,
it really is just a matter of copying the right files into place.
# .travis.yml
before_install:
- cp gemfiles/Gemfile.$DB Gemfile
install: bundle install --jobs=3 --retry=3 --without production
before_script:
- cp .env.test.$DB .env.test
script:
- 'HANAMI_ENV=test bundle exec hanami db create'
- 'HANAMI_ENV=test bundle exec hanami db migrate'
- 'bundle exec rake test'
That’s all it takes to run your tests against multiple databases with Hanami!
Bonus: test against multiple rubies#
Testing against multiple databases is cool, but it’s also very well possible that end-users will not be using the greatest and latest ruby version. Firefly currently support the latest 2.2.x and 2.3. versions of Ruby. Travis supports this out of the box:
# .travis.yml
rvm:
- 2.2.4
- 2.3.0
This, in combination with our database setup, will trigger six builds. Sqlite, MySQL and PostgreSQL builds on ruby-2.2.4 and on ruby-2.3.0.
I hope you liked this post. Happy coding and keep testing!