Using Rails 5.1 System Tests with Docker

Pablo Acuña
3 min readJun 18, 2017

--

I’ve been using Docker for my Rails development environment for a while now. This means I’ve had to address a lot of different issues and learn some of the good and bad practices around containerized systems.

I’m also a heavy advocate of TDD and testing in general. I think the time you lose writing tests is actually less than the time you spend testing your application by hand to see if your new method or feature works as expected. And of course, in the future when you need to modify your code, good luck with testing you didn’t break anything if you don’t have a decent test suite.

With that said, I’ve never been a big fan of acceptance testing. Maybe it’s because every time I’ve tried has been a painful experience. Most of this was probably due to my inexperience with them. On the other side, when I started with Unit and Functional testing, everything worked just fine. And the integration issues were always minor, whereas with acceptance testing, it was always hard.

All of that changed when I started to use new Rails 5.1 System Tests feature. I know that under the hood, thanks to the great core team, the integration with the gems is rock solid. I think convention over configuration specifically with these kind of topics is really important.

Before this version you had to add Capybara, Database cleaner and maybe other gems to start writing your acceptance tests. And then you had to modify your helpers, select a database cleaning strategy, etc. It just didn’t feel clean to me. That’s why I tried to avoid that kind of testing whenever I could.

At first, with this new Rails feature, I was a little bit confused about how to use it with Docker. Because you need to setup a driver such a Chrome or Firefox. And you don’t want to add all those dependencies to your Dockerfile. So I thought, hey! Probably someone already built a Docker image with some of those drives, and I found this.

So using that image and doing some minor adjustments, you can have an independent container running your driver and not pollute your main application Dockerfile with all those dependencies.

I ended up having a docker-compose.yml file similar to this:

version: '2'
services:
chrome:
image: selenium/standalone-chrome-debug
volumes:
- /dev/shm:/dev/shm
myapp:
build:
context: .
dockerfile: Dockerfile.development
depends_on:
- chrome

And then you can configure your test/application_system_test_case.rb to use this chrome service instead of looking for a local instance:

require "test_helper"
require "socket"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400], options: {url: "http://chrome:4444/wd/hub"}
include Devise::Test::IntegrationHelpers def setup
host! "http://#{IPSocket.getaddress(Socket.gethostname)}"
super
end
end

As you can see, we are telling Rails to use our other service container address for chrome, which is chrome.

The setup part is necessary. If you don’t change your host address, you’ll basically run your tests using localhost as your base path. So for example, if you try to visit something with capybara, let’s say articles_path, capybara will try to go to http://localhost/articles instead your container’s IP address.

I’m sure there are some other ways to set the container’s IP address but this one worked for me immediately. And I have to thank to the creator of this pull request for the implementation of these extra options and the basic idea.

So that’s it! Now you can run your system tests in a Dockerize environment:

root@9bb7de76347b:/home/app/webapp# RAILS_ENV=test bin/rails test:system
Run options: --seed 10639
# Running:Puma starting in single mode...
* Version 3.8.2 (ruby 2.4.0-p0), codename: Sassy Salamander
* Min threads: 0, max threads: 1
* Environment: test
* Listening on tcp://0.0.0.0:45365
Use Ctrl-C to stop
................
Finished in 12.000958s, 1.3332 runs/s, 2.2498 assertions/s.
16 runs, 27 assertions, 0 failures, 0 errors, 0 skips
root@9bb7de76347b:/home/app/webapp#

The cool thing is that I had to configure nothing else. Rails will clean the database in between runs, it’ll start the puma server to Run the tests and any other thing that was impeding me to adopt this important part of testing.

If you need help with Docker and Rails, check out my Dckerize project to generate all the necessary boilerplate.

Thanks for reading!

--

--