I write tests using Minitest and routinely reference EvilMartians System of a Test blog post for best practices for system tests. In this blog post, they also have an opinionated way of setting up System Tests.

EvilSystems offers 3 distinct advantages over the setup provided by EvilMartians System of a Test:

1.) The blog post was built with RSpec in mind, but we use Minitest here!

2.) Constantly copying 5 files over into every new Rails app is annoying. Lets make that easier!

3.) File changes can end up out of sync, a gem makes sure updates can be pushed to all users.

EvilSystems is a quick, easy, reusable way to apply the SoaT concepts and settings to projects for system tests using Minitest.

Full API documentation can be found here:



bundle add evil_systems --group=test

Make sure the following 3 gems are in your Gemfile as well:

# Gemfile

group :test do
  gem 'capybara'
  gem 'cuprite' # Optional
  gem 'selenium-webdriver' # Not required if using Cuprite and using Rails >= 6.1

Note: bundle add by default appends the gem to the bottom of your Gemfile, which means not in the test group of gems. If the capybara gem is in the test group, but evil_systems is not, you will not be able to load your application in production. Be sure that evil_systems is placed in the same group as capybara (we recommend the test group).



Navigate to test/application_system_test_case.rb in your Rails app.

Setup your file like so:

# test/application_system_test_case.rb

require 'test_helper'

# 'capybara' and 'capybara/cuprite' need to be defined for EvilSystems to work properly.
require 'capybara'
require 'capybara/cuprite'

require 'evil_systems'


class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :evil_cuprite

  include EvilSystems::Helpers


Maybe in the future?

Whats included?


EvilSystems.initial_setup takes three keyword arguments, :task, and silent, skip_task.

They all have to do with precompiling assets.

:silent by default is set to true and will only tell you when assets are compiling, and how long the compilation took.

:task defaults to assets:precompile, System of a test uses webpacker:compile.

:skip_task when true says you dont want to run any Rake tasks. Default is false


EvilSystems.initial_setup(task: "webpacker:compile", silent: false)


  • [x] - Automatically registers a :evil_cuprite driver if Capybara::Cuprite is defined.

  • [x] - Automatically sets Capybara's default and javascript driver to :evil_cuprite

  • [x] - Automatically sets Capybara.app_host

How `app_host` is set `app_host` will first use `ENV["APP_HOST"]` then falls back to the systems `hostname` if the `APP_HOST` ENV var is not defined. If neither are defined, it will then default to `""`
  • [x] - Capybara.server_host = "" Make server listening on all hosts

  • [x] - Capybara.default_max_wait_time = 2 Dont spend forever waiting for matchers

  • [x] - Capybara.default_normalize_ws = true normalizes whitespace in has_text? and similar matchers.

  • [x] - Sets the Capybara.save_path Uses ENV["CAPYBARA_ARTIFACTS"] and falls back to "./tmp/capybara"

  • [x] - Sets a REMOTE_CHROME instance if a ENV["CHROME_URL"] is found

  • [x] - Prepends a last_used_session attribute accessor to Capybara.



Automatically includes ActionView::RecordIdentifier if Rails is defined.

Also includes:


Regular Helpers

# The full path to be prepended to a screen shot

# The relative path to be prepended to a screenshot message to make it clickable

# Make failure screenshots compatible with multi-session setup

# Prepends a '#' to the +dom_id+ method provided by Rails


# Small wrapper around Capybara.using_session thats easy to call from an instance
within_session(name_or_session, &block)

Cuprite Helpers

# pauses the page

# Opens a Pry or IRB repl. Will use Pry if Pry is defined, fallsback
# to debugging with IRB
debug binding

# waits to make sure theres no active connections.

Env Variables

ENV variables used by this gem.

ENV["APP_HOST"] # used for Capybara.app_host
ENV["CAPYBARA_ARTIFACTS"] # used for Capybara.save_path
ENV["CHROME_URL"] # used for setting a remote chrome instance for Cuprite
ENV["PROCESS_TIMEOUT"] # How long to wait before killing the process, default is 5 seconds
ENV["CI"] # Whether or not to run Cuprite in headless mode, defaults to true.
ENV["SLOWMO"] # Delay in seconds before sending a command (default 0). Also see https://github.com/rubycdp/ferrum#customization
ENV["DISABLE_ANIMATION"] # Configure whether Capybara should render animations, default is true

I don't want to use Cuprite.

Thats fine! I totally get it. Selenium is battle tested. Simply remove the require "capybara/cuprite" line and EvilSystems will detect that Cuprite is not defined and not setup a driver for you and not include Cuprite helpers.

Omissions and differences

  • Will use assets:precompile instead of webpacker:compile before systems tests (configurable)

  • Does not set the Rails.application.default_url_options[:host] due to parallelization issues found while testing the dummy app.


The gem is available as open source under the terms of the MIT License.