.claude_37signals/skills/testing-patterns/SKILL.md
Writes Minitest tests with fixtures following 37signals conventions. Uses Minitest (not RSpec) and fixtures (not factories). Use when writing tests, adding test coverage, or creating fixtures. WHEN NOT: For RSpec or FactoryBot patterns (this project uses Minitest + fixtures exclusively). For test configuration/CI setup (see project docs).
npx skillsauth add ThibautBaissac/rails_ai_agents testing-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert Rails testing architect specializing in Minitest with fixtures.
Minitest is plenty. Fixtures are faster.
Tech Stack: Minitest 5.20+, Rails 8.2, YAML fixtures
Location: test/models/, test/controllers/, test/system/, test/integration/
bin/rails test -- Full suitebin/rails test test/models/card_test.rb -- Specific filebin/rails test test/models/card_test.rb:14 -- Specific linebin/rails test:system -- System testsbin/rails test:parallel -- Parallel executionrequire "test_helper"
class CardTest < ActiveSupport::TestCase
setup do
@card = cards(:logo)
@user = users(:david)
Current.user = @user
Current.account = @card.account
end
teardown do
Current.reset
end
test "fixtures are valid" do
assert @card.valid?
end
test "closing card creates closure record" do
assert_difference -> { Closure.count }, 1 do
@card.close(user: @user)
end
assert @card.closed?
assert_equal @user, @card.closed_by
end
test "open scope excludes closed cards" do
@card.close
assert_not_includes Card.open, @card
assert_includes Card.closed, @card
end
end
require "test_helper"
class CardsControllerTest < ActionDispatch::IntegrationTest
setup do
@card = cards(:logo)
sign_in_as users(:david)
end
test "should create card" do
assert_difference -> { Card.count }, 1 do
post board_cards_path(@card.board), params: {
card: { title: "New card", column_id: @card.column_id }
}
end
assert_redirected_to card_path(Card.last)
end
test "requires authentication" do
sign_out
get card_path(@card)
assert_redirected_to new_session_path
end
end
# test/test_helper.rb
class ActionDispatch::IntegrationTest
def sign_in_as(user)
session_record = user.identity.sessions.create!
cookies.signed[:session_token] = session_record.token
Current.user = user
Current.identity = user.identity
Current.session = session_record
end
def sign_out
cookies.delete(:session_token)
Current.reset
end
end
class ActiveSupport::TestCase
fixtures :all
parallelize(workers: :number_of_processors)
end
# Record count changes
assert_difference -> { Card.count }, 1 do ... end
# Attribute updates
@card.close
assert @card.closed?
assert_equal @user, @card.closed_by
# Errors
assert_raises ActiveRecord::RecordInvalid do
Card.create!(title: nil)
end
# Collections
assert_includes Card.open, @card
refute_includes Card.closed, @card
# HTTP responses
assert_response :success
assert_redirected_to card_path(Card.last)
# DOM assertions
assert_select "h1", "Cards"
assert_select ".card", count: 3
# Jobs and emails
assert_enqueued_with job: NotifyRecipientsJob do ... end
assert_emails 1 do ... end
# BAD: Using factories
let(:card) { FactoryBot.create(:card) }
# GOOD: Use fixtures
setup { @card = cards(:logo) }
# BAD: Testing implementation
test "calls create_closure" do
@card.expects(:create_closure!)
@card.close
end
# GOOD: Test behavior
test "closing creates closure" do
@card.close
assert @card.closed?
end
# BAD: Creating data when fixtures exist
setup { @user = User.create!(name: "Test") }
# GOOD: Use fixtures
setup { @user = users(:david) }
# BAD: Testing Rails functionality
test "validates presence of title" do ...
# GOOD: Only test custom validations
test "validates title doesn't contain profanity" do ...
references/fixture-patterns.md -- YAML fixture patterns, ERB, UUID fixtures, associationsreferences/controller-tests.md -- Controller/integration test patterns, Turbo Stream assertionsreferences/system-tests.md -- Capybara system test patterns, setup, assertionsdevelopment
Creates Turbo Streams, Turbo Frames, and morphing patterns for real-time UI updates. Use when adding real-time updates, partial page rendering, form submissions, or broadcasting. WHEN NOT: For Stimulus JavaScript controllers (see stimulus-patterns skill). For general view conventions (see rules/views.md).
tools
Builds focused, single-purpose Stimulus controllers for progressive enhancement. Use when adding JavaScript behavior, UI interactions, form enhancements, or building reusable client-side components. WHEN NOT: For Turbo Stream/Frame patterns (see turbo-patterns skill). For server-side view logic (see rules/views.md).
testing
Implements the state-as-records-not-booleans pattern for rich state tracking. Use when modeling state changes, replacing boolean flags with record-based state, or when user mentions state records, closures, publications, or toggling state. WHEN NOT: Technical flags like cached/processed (use booleans), concern extraction (use concern-patterns), general model work (use model-patterns).
data-ai
Implements URL-based multi-tenancy with account scoping, membership patterns, and data isolation following 37signals patterns. Use when setting up multi-tenant architecture, account isolation, membership management, or when user mentions multi-tenancy, accounts, or tenant separation. WHEN NOT: For basic model setup without tenancy (use model-patterns), for auth/session setup (use auth-setup).