This skill should be used when the user asks about testing, test strategy, system tests, integration tests, fixtures, test organization, or Rails testing philosophy. Trigger phrases include "testing", "tests", "fixtures", "system tests", "integration tests", "test strategy", "TDD", "test organization", "Minitest", "test helpers", "factories vs fixtures".
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/model-test.rbexamples/system-test.rbreferences/fixtures-guide.mdreferences/system-tests.mdExpertise in Rails testing philosophy, system tests, fixtures, and test organization for Rails 8 applications.
Fixtures are fast, predictable, and version controlled. They're the Rails way. Factory Bot adds overhead, complexity, and slower tests. Start with fixtures, stay with fixtures.
| Test Type | Use For | Speed |
|---|---|---|
| System | Critical user journeys, JS interactions | Slowest |
| Integration | API endpoints, request/response cycles | Medium |
| Model | Business logic, validations, scopes | Fastest |
# test/application_system_test_case.rb
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end
# test/system/articles_test.rb
require "application_system_test_case"
class ArticlesTest < ApplicationSystemTestCase
setup do
@user = users(:steve)
@article = articles(:rails_guide)
end
test "visiting the index" do
sign_in_as @user
visit articles_url
assert_selector "h1", text: "Articles"
assert_text @article.title
end
test "creating an article" do
sign_in_as @user
visit new_article_url
fill_in "Title", with: "New Article"
fill_in "Body", with: "Article content here"
click_on "Create Article"
assert_text "Article created"
assert_text "New Article"
end
test "editing an article with turbo" do
sign_in_as @user
visit article_url(@article)
click_on "Edit"
within turbo_frame("article_#{@article.id}") do
fill_in "Title", with: "Updated Title"
click_on "Save"
end
assert_text "Updated Title"
assert_no_selector "form" # Frame closed
end
end
# test/support/system_test_helpers.rb
module SystemTestHelpers
def sign_in_as(user)
visit login_url
fill_in "Email", with: user.email
fill_in "Password", with: "password"
click_on "Sign in"
assert_text "Signed in successfully"
end
def sign_out
click_on "Sign out"
assert_text "Signed out"
end
end
# Include in test_helper.rb
class ActionDispatch::SystemTestCase
include SystemTestHelpers
end
# test/fixtures/users.yml
steve:
name: Steve
email: steve@example.com
password_digest: <%= BCrypt::Password.create('password') %>
admin: false
admin:
name: Admin User
email: admin@example.com
password_digest: <%= BCrypt::Password.create('password') %>
admin: true
# test/fixtures/articles.yml
rails_guide:
title: Getting Started with Rails
body: Rails is a web framework...
user: steve # References users(:steve)
published_at: <%= 1.day.ago %>
draft_article:
title: Work in Progress
body: Not ready yet...
user: steve
published_at: null
# test/fixtures/comments.yml
first_comment:
content: Great article!
article: rails_guide
user: admin
# test/fixtures/articles.yml
<% 5.times do |i| %>
article_<%= i %>:
title: Article <%= i %>
body: Content for article <%= i %>
user: steve
published_at: <%= i.days.ago %>
<% end %>
| Aspect | Fixtures | Factory Bot |
|---|---|---|
| Speed | Fast (loaded once) | Slow (created per test) |
| Predictability | Same data every run | Generated each time |
| Database IDs | Fixed, predictable | Random, varying |
| Setup | None beyond YAML | Gem, config, definitions |
| Associations | Natural references | Traits, sequences, callbacks |
# test/models/user_test.rb
require "test_helper"
class UserTest < ActiveSupport::TestCase
test "valid with all attributes" do
user = User.new(
name: "Test User",
email: "test@example.com",
password: "password"
)
assert user.valid?
end
test "invalid without email" do
user = users(:steve)
user.email = nil
assert_not user.valid?
assert_includes user.errors[:email], "can't be blank"
end
test "email must be unique" do
duplicate = User.new(
name: "Other",
email: users(:steve).email,
password: "password"
)
assert_not duplicate.valid?
assert_includes duplicate.errors[:email], "has already been taken"
end
end
test "published scope returns only published articles" do
published = Article.published
assert_includes published, articles(:rails_guide)
assert_not_includes published, articles(:draft_article)
end
test "by_author scope filters by user" do
steve_articles = Article.by_author(users(:steve))
assert steve_articles.all? { |a| a.user == users(:steve) }
end
test "completing an order updates status and stock" do
order = orders(:pending)
assert_changes -> { order.reload.status }, from: "pending", to: "completed" do
assert_changes -> { products(:widget).reload.stock_count }, by: -1 do
order.complete!
end
end
assert_not_nil order.completed_at
end
test "cannot complete already completed order" do
order = orders(:completed)
assert_raises Order::AlreadyCompletedError do
order.complete!
end
end
# test/controllers/articles_controller_test.rb
require "test_helper"
class ArticlesControllerTest < ActionDispatch::IntegrationTest
setup do
@article = articles(:rails_guide)
@user = users(:steve)
end
test "should get index" do
get articles_url
assert_response :success
assert_select "h1", "Articles"
end
test "should create article when signed in" do
sign_in_as @user
assert_difference("Article.count") do
post articles_url, params: {
article: { title: "New", body: "Content" }
}
end
assert_redirected_to article_url(Article.last)
end
test "should not create article when not signed in" do
post articles_url, params: {
article: { title: "New", body: "Content" }
}
assert_redirected_to login_url
assert_equal "Please sign in", flash[:alert]
end
end
# test/controllers/api/articles_controller_test.rb
class Api::ArticlesControllerTest < ActionDispatch::IntegrationTest
test "returns articles as JSON" do
get api_articles_url, as: :json
assert_response :success
json = JSON.parse(response.body)
assert_kind_of Array, json
assert json.any? { |a| a["title"] == articles(:rails_guide).title }
end
test "creates article with valid token" do
post api_articles_url,
params: { article: { title: "API Article", body: "Content" } },
headers: { "Authorization" => "Bearer #{api_tokens(:steve).token}" },
as: :json
assert_response :created
assert_equal "API Article", JSON.parse(response.body)["title"]
end
end
# test/test_helper.rb
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
fixtures :all
def sign_in_as(user)
post login_url, params: { email: user.email, password: "password" }
end
end
# test/support/custom_assertions.rb
module CustomAssertions
def assert_published(article)
assert article.published?, "Expected article to be published"
assert_not_nil article.published_at
end
def assert_validation_error(record, attribute, message = nil)
assert_not record.valid?
if message
assert_includes record.errors[attribute], message
else
assert record.errors[attribute].any?
end
end
end
class ActiveSupport::TestCase
include CustomAssertions
end
# test/test_helper.rb
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
end
Tests run in transactions and roll back automatically - fixtures are shared, no cleanup needed.
# test/test_helper.rb
class ActiveSupport::TestCase
include ActiveJob::TestHelper
end
# In tests
test "sends welcome email after signup" do
assert_enqueued_with(job: WelcomeEmailJob) do
User.create!(name: "New", email: "new@example.com", password: "password")
end
end
# Avoid this pattern
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
password { "password" }
end
end
# Every test creates new records - slow!
let(:user) { create(:user) }
# BAD: Testing internal implementation
test "calls send_email method" do
user = users(:steve)
mock = Minitest::Mock.new
mock.expect :call, true
User.stub :send_email, mock do
user.welcome!
end
mock.verify
end
# GOOD: Testing behavior
test "welcome sends email" do
assert_emails 1 do
users(:steve).welcome!
end
end
test/
├── test_helper.rb
├── application_system_test_case.rb
├── fixtures/
│ ├── users.yml
│ └── articles.yml
├── models/
│ ├── user_test.rb
│ └── article_test.rb
├── controllers/
│ └── articles_controller_test.rb
├── system/
│ └── articles_test.rb
├── integration/
│ └── user_flows_test.rb
└── support/
├── system_test_helpers.rb
└── custom_assertions.rb
bin/rails test # All tests except system
bin/rails test:system # System tests only
bin/rails test:all # Everything
bin/rails test test/models # Just model tests
bin/rails test test/models/user_test.rb:25 # Specific line
references/fixtures-guide.md - Advanced fixture patternsreferences/system-tests.md - Browser testing patternsexamples/model-test.rb - Complete model testexamples/system-test.rb - System test patterns