| name | minitest-coder |
| description | 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. |
| allowed-tools | Read, Write, Edit, MultiEdit, Grep, Glob, Bash, WebSearch |
Minitest Coder
Core Philosophy
- AAA Pattern: Arrange-Act-Assert structure for clarity
- Behavior over Implementation: Test what code does, not how
- Isolation: Tests should be independent
- Descriptive Names: Clear test descriptions
- Coverage: Test happy paths AND edge cases
- Fast Tests: Minimize database operations
- Fixtures: Use fixtures for test data
Minitest Styles
| Style | Best For | Syntax |
|---|---|---|
| Traditional | Simple unit tests | test "description" |
| Spec | Complex scenarios with contexts | describe/it with let/subject |
Traditional Style
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
Spec Style
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 Organization
File Structure
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
Naming Conventions
- Mirror app structure:
app/models/user.rb→test/models/user_test.rb - Use fully qualified namespace:
class Users::ProfileServiceTest - Don't add
require 'test_helper'(auto-imported)
Spec Style Patterns
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
Fixtures
# 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
Mocking and Stubbing
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
Assertions Quick Reference
Basic Assertions
# 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
Rails Assertions
# 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)
AAA Pattern
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
Test Coverage Standards
| 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 |
Coverage Example
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
Anti-Patterns
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 |
Best Practices Checklist
Organization:
- Files mirror app structure
- NOT adding
require 'test_helper' - Using fully qualified namespace
Style Choice:
- Traditional for simple tests
- Spec for complex contexts
- Max 3 nesting levels
Test Data:
- Using fixtures (not factories)
- Using
letfor shared data - Using
subjectfor method under test
Assertions:
- Correct assertion methods
- Rails helpers (
assert_changes,assert_difference) - Testing behavior, not implementation
Coverage:
- Happy path tested
- Sad path tested
- Edge cases covered
When to Choose Style
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.