End To End Tests with Minitest and Rails
willow.camp has a small system test suite that runs on CI. It used to use the out-of-the-box settings that come with Rails: Capybara, Selenium, etc.
I noticed a flaky test on CI, which launched a journey to replace Selenium with Cuprite.
Fixing the Flaky Test
I worked through the flaky test with Claude Code, and as Claude was running the tests, I noticed this pop-up from Chrome.
A data breach on a site or app exposed your password…
I don’t use Chrome very often, so this jumped out at me. Of course, I use the oh-so-secret password “password” in my test suite, and Chrome flagged it as unsafe.
Disabling password leak detection fixed this for me.
browser_options: { "disable-features": "PasswordLeakDetection" }
But I still got timeouts on CI that looked like the tests were running before the browser was ready.
Waiting for CI is a bit painful. You can see my hilariously bad Git history with Claude’s gleeful overconfidence:
* 0c514d1 - nosandbox (15 minutes ago) <Cassia Scheffer>
...
* 57ac55b - bump timeout to 30 sec for ci (21 minutes ago)
* 9d6e86e - rewirite cuprite/capybara configs (26 minutes ago)
* 39f3238 - fix: simplify Cuprite configuration following best practices (10 hours ago)
* 68bfd53 - fix: simplify Cuprite configuration and fix CI timeout issues (24 hours ago)
* cebfae7 - fix: resolve Cuprite websocket timeout issues in CI (24 hours ago)
* dc288b7 - fix: make Cuprite configuration more robust for CI environments (24 hours ago)
...
* e5bb4df - fix: increase Cuprite timeout and add CI-specific settings for GitHub Acti>
...
* cb36eaa - feat: migrate system tests from Selenium to Cuprite (35 hours ago)
* 39f0ff1 - disable selenium chrome password detection (35 hours ago)
That Looks Bad, Cassia. Why Would You Let Claude Do That!?
Because Claude is faster at making mistakes than I am, and I am tired of waiting for CI. Making mistakes quickly means speedier feedback. So I sent Claude off on a fool’s errand to help me learn from the mistakes I would otherwise have made myself.
In the meantime, I read the Evil Martians article about better system tests.
Using Cuprite in Rails with Minitest
The Evil Martians article on system tests uses RSpec, but willow.camp uses Minitest. Here’s what I had to change to make it work with Minitest.
1. PrecompileAssets becomes a module and gets called when setup runs.
# Precompile assets before running tests to avoid timeouts.
# Do not precompile if webpack-dev-server is running (NOTE: MUST be launched with RAILS_ENV=test)
module PrecompileAssets
def self.setup
# Check if we're running system tests by looking at the test files being loaded
running_system_tests = caller.any? { |line| line.include?("test/system") } ||
ARGV.any? { |arg| arg.include?("test/system") } ||
ENV["RAILS_TEST_TYPE"] == "system"
unless running_system_tests
puts "\n🚀️️ No system test selected. Skip assets compilation.\n"
return
end
puts "\n🐢 Precompiling assets.\n"
original_stdout = $stdout.clone
start = Time.current
begin
$stdout.reopen(File.new(File::Constants::NULL, "w"))
require "rake"
Rails.application.load_tasks
Rake::Task["assets:precompile"].invoke
ensure
$stdout.reopen(original_stdout)
puts "Finished in #{(Time.current - start).round(2)} seconds"
end
end
end
# Run the setup when this file is loaded
PrecompileAssets.setup if defined?(Rails)
2. BetterRailsSystemTests Becomes a Moduel
module BetterRailsSystemTests
# Make failure screenshots compatible with multi-session setup.
def take_screenshot
return super unless Capybara.last_used_session
Capybara.using_session(Capybara.last_used_session) { super }
end
end
3. Modules are included in ApplicationSystemTestCase
require "test_helper"
require "capybara/cuprite"
require_relative "system/system_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
include BetterRailsSystemTests
include CupriteHelpers
driven_by Capybara.javascript_driver
def setup
super
# Use JS driver always
# Store original host for cleanup
@original_host = Rails.application.default_url_options[:host]
# Make urls in mailers contain the correct server host.
# This is required for testing links in emails (e.g., via capybara-email).
Rails.application.default_url_options[:host] = Capybara.server_host
end
def teardown
# Restore original host
Rails.application.default_url_options[:host] = @original_host
super
end
end
Flakiness Still Included on CI
I still see a bit of system test flakiness on CI, but far less than before. I am running on the default GitHub Actions runner, which is pretty small. So there’s a good chance that the flakiness is due to low resources on the machine. So far, tests have consistently passed when I run these on my M1 MacBook Air.
I’ll dig into these flaky tests another day! But this looks like an improvement overall to me!