devroom.io/content/posts/2016-04-15-hanami-and-multi-database-testing-with-travis.md

192 lines
5.7 KiB
Markdown
Raw Normal View History

+++
date = "2016-04-15"
title = "Hanami and Multi-Database Testing with Travis"
tags = ["hanami", "travis", "testing"]
description = "Hanami: good! Travis: good! Testing your code against multiple databases: priceless!"
2016-08-24 11:03:32 +00:00
slug = "hanami-and-multi-database-testing-with-travis"
+++
2016-04-19 09:07:50 +00:00
_This is a re-post of my article over at [Kabisa](https://kabisa.nl)'s [The Guild](https://www.theguild.nl/hanami-and-multi-database-testing-with-travis)._
I've been busy rewriting [Firefly](https://github.com/ariejan/firefly) for a while now using
[Hanami](http://hanamirb.org). 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](https://heroku.com) 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](http://sequel.jeremyevans.net/). 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](https://travis-ci.org) 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.
2017-03-20 15:35:19 +00:00
``` ruby
# 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:
2017-03-20 15:35:19 +00:00
``` shell
# .env.test.sqlite
FIREFLY_DATABASE_URL="sqlite://db/firefly_development.sqlite3"
```
For MySQL:
2017-03-20 15:35:19 +00:00
``` shell
# .env.test.mysql
FIREFLY_DATABASE_URL="mysql://root@localhost/firefly_test"
```
For Postgres:
2017-03-20 15:35:19 +00:00
``` shell
# .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:
2017-03-20 15:35:19 +00:00
``` ruby
# gemfiles/Gemfile.sqlite
gem 'sqlite3'
```
For MySQL:
2017-03-20 15:35:19 +00:00
``` ruby
# gemfiles/Gemfile.mysql
gem 'mysql'
```
For Postgres:
2017-03-20 15:35:19 +00:00
``` ruby
# 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:
2017-03-20 15:35:19 +00:00
``` yaml
# .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:
2017-03-20 15:35:19 +00:00
``` yaml
# .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.
2017-03-20 15:35:19 +00:00
``` yaml
# .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:
2017-03-20 15:35:19 +00:00
``` yaml
# .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.
![Travis builds](/img/travis-firefly-builds.png)
I hope you liked this post. Happy coding and keep testing!