This skill guides writing comprehensive Minitest tests for Ruby and Rails applications. Use when creating test files, writing test cases, or testing new features. Covers both traditional and spec styles, fixtures, mocking, and Rails integration testing patterns.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
references/testing-tips.mdresources/anti-patterns.mdresources/spec-patterns.md| Style | Best For | Syntax |
|---|---|---|
| Traditional | Simple unit tests | test "description" |
| Spec | Complex scenarios with contexts | describe/it with let/subject |
class UserTest < ActiveSupport::TestCase
test "validates presence of name" do
user = User.new
assert_not user.valid?
assert_includes user.errors[:name], "can't be blank"
end
end
class UserTest < ActiveSupport::TestCase
describe "#full_name" do
subject { user.full_name }
let(:user) { User.new(first_name: "Buffy", last_name:) }
describe "with last name" do
let(:last_name) { "Summers" }
it "returns full name" do
assert_equal "Buffy Summers", subject
end
end
end
end
test/
├── models/ # Model unit tests
├── services/ # Service object tests
├── integration/ # Full-stack tests
├── mailers/ # Mailer tests
├── jobs/ # Background job tests
├── fixtures/ # Test data
└── test_helper.rb # Configuration
app/models/user.rb → test/models/user_test.rbclass Users::ProfileServiceTestrequire 'test_helper' (auto-imported)See resources/spec-patterns.md for detailed examples.
| Pattern | Use Case |
|---|---|
subject { ... } | Method under test |
let(:name) { ... } | Lazy-evaluated data |
describe "context" | Group related tests (max 3 levels) |
before { ... } | Complex setup |
describe "#process" do
subject { processor.process }
let(:processor) { OrderProcessor.new(order) }
let(:order) { orders(:paid_order) }
it "succeeds" do
assert subject.success?
end
end
# test/fixtures/users.yml
alice:
name: Alice Smith
email: alice@example.com
created_at: <%= 2.days.ago %>
class UserTest < ActiveSupport::TestCase
fixtures :users
test "validates uniqueness" do
duplicate = User.new(email: users(:alice).email)
assert_not duplicate.valid?
end
end
See resources/spec-patterns.md for detailed examples.
| Method | Purpose |
|---|---|
Object.stub :method, value | Stub return value |
Minitest::Mock.new | Verify method calls |
test "processes payment" do
PaymentGateway.stub :charge, true do
processor = OrderProcessor.new(order)
assert processor.process
end
end
# Boolean
assert user.valid?
assert_not user.admin?
# Equality
assert_equal "Alice", user.name
assert_nil user.deleted_at
# Collections
assert_includes users, admin_user
assert_empty order.items
# Exceptions
assert_raises ActiveRecord::RecordInvalid do
user.save!
end
# Changes
assert_changes -> { user.reload.status }, to: "active" do
user.activate!
end
assert_difference "User.count", 1 do
User.create(name: "Charlie")
end
# Responses
assert_response :success
assert_redirected_to user_path(user)
test "processes refund" do
# Arrange
order = orders(:completed_order)
original_balance = order.user.account_balance
# Act
result = order.process_refund
# Assert
assert result.success?
assert_equal "refunded", order.reload.status
end
Spec style with subject:
describe "#process_refund" do
subject { order.process_refund }
let(:order) { orders(:completed_order) }
it "updates status" do
subject
assert_equal "refunded", order.reload.status
end
it "credits user" do
assert_changes -> { order.user.reload.account_balance }, by: order.total do
subject
end
end
end
| Type | Test For |
|---|---|
| Models | Validations, associations, scopes, callbacks, methods |
| Services | Happy path, sad path, edge cases, external integrations |
| Controllers | Status codes, redirects, parameter handling |
| Jobs | Execution, retry logic, error handling |
class UserTest < ActiveSupport::TestCase
# Validations
test "validates presence of name" do
user = User.new(email: "test@example.com")
assert_not user.valid?
assert_includes user.errors[:name], "can't be blank"
end
# Methods
describe "#full_name" do
subject { user.full_name }
let(:user) { User.new(first_name: "Alice", last_name: "Smith") }
it "returns full name" do
assert_equal "Alice Smith", subject
end
describe "without last name" do
let(:user) { User.new(first_name: "Alice") }
it "returns first name only" do
assert_equal "Alice", subject
end
end
end
end
See resources/anti-patterns.md for detailed examples.
| Anti-Pattern | Why Bad |
|---|---|
require 'test_helper' | Auto-imported |
| >3 nesting levels | Unreadable output |
@ivars instead of let | State leakage |
Missing subject | Repetitive code |
assert x.include?(y) | Use assert_includes |
| Testing private methods | Implementation coupling |
| Not using fixtures | Slow tests |
Organization:
require 'test_helper'Style Choice:
Test Data:
let for shared datasubject for method under testAssertions:
assert_changes, assert_difference)Coverage:
Traditional: Simple validations, straightforward tests, no shared setup
Spec: Multiple contexts, lazy evaluation needed, nested scenarios, reusable subject
Can mix both in the same file if it improves clarity.