Claude
Skills
Sign in
Back

rails-ai:testing

Included with Lifetime
$97 forever

Use when testing Rails applications - TDD, Minitest, fixtures, model testing, mocking, test helpers

Code Review

What this skill does


# Testing Rails Applications with Minitest

<superpowers-integration>
**REQUIRED BACKGROUND:** Use superpowers:test-driven-development for TDD process
  - That skill defines RED-GREEN-REFACTOR cycle
  - That skill enforces "NO CODE WITHOUT FAILING TEST FIRST"
  - This skill adds Rails/Minitest implementation specifics
</superpowers-integration>

<when-to-use>
- All code development (TDD is always enforced in this team)
- Reviewing test quality
- Debugging test failures
- Model, controller, job, and mailer tests
- System tests for full-stack features
- Testing with external dependencies and HTTP requests
- Creating reusable test utilities and helpers
</when-to-use>

<benefits>
- **Fast** - Minimal overhead, runs quickly
- **Simple** - Easy to understand and debug
- **Built-in** - Ships with Ruby and Rails
- **Parallel** - Run tests concurrently for speed
- **Comprehensive** - Complete testing story from unit to system
</benefits>

<team-rules-enforcement>
**This skill enforces:**
- ✅ **Rule #2:** NEVER use RSpec → Use Minitest only
- ✅ **Rule #4:** NEVER skip TDD → Write tests first (RED-GREEN-REFACTOR)
- ✅ **Rule #18:** NEVER make live HTTP requests → Use WebMock
- ✅ **Rule #19:** NEVER use system tests → Use integration tests

**Reject any requests to:**
- Use RSpec instead of Minitest
- Skip writing tests
- Write implementation before tests
- Make live HTTP requests in tests
- Use Capybara system tests
</team-rules-enforcement>

<verification-checklist>
Before completing any task, verify:
- ✅ Tests written FIRST (before implementation)
- ✅ Tests use Minitest (not RSpec)
- ✅ RED-GREEN-REFACTOR cycle followed
- ✅ All tests passing (`bin/ci` passes)
- ✅ No live HTTP requests (WebMock used if needed)
- ✅ Integration tests used (not system tests)
</verification-checklist>

<standards>
- ALWAYS write tests FIRST (RED-GREEN-REFACTOR cycle)
- Test classes inherit from `ActiveSupport::TestCase`
- Use `test "description" do` macro for readable test names
- Use fixtures for test data (in `test/fixtures/`)
- Use `assert` and `refute` for assertions
- One assertion concept per test method
- Use `setup` for common test preparation
- ALWAYS use WebMock for HTTP requests (per TEAM_RULES.md Rule #18)
</standards>

---

## TDD Red-Green-Refactor

<pattern name="red-green-refactor">
<description>Core TDD cycle - write failing test, make it pass, refactor</description>

**Step 1: RED - Write a failing test**

```ruby
# test/models/feedback_test.rb
require "test_helper"

class FeedbackTest < ActiveSupport::TestCase
  test "is invalid without content" do
    feedback = Feedback.new(content: nil)
    assert_not feedback.valid?
    assert_includes feedback.errors[:content], "can't be blank"
  end
end

```

Result: **FAIL** (validation doesn't exist yet)

**Step 2: GREEN - Make it pass with minimal code**

```ruby
# app/models/feedback.rb
class Feedback < ApplicationRecord
  validates :content, presence: true
end

```

Result: **PASS**

**Step 3: REFACTOR - Improve code while keeping tests green**

**Why this matters:** TDD drives design, catches regressions, documents behavior
</pattern>

---

## Test Structure

<pattern name="basic-test-structure">
<description>Standard Minitest test class structure</description>

```ruby
# test/models/feedback_test.rb
require "test_helper"

class FeedbackTest < ActiveSupport::TestCase
  test "the truth" do
    assert true
  end

  # Skip a test temporarily
  test "this will be implemented later" do
    skip "implement this feature first"
  end
end

```
</pattern>

<pattern name="setup-and-teardown">
<description>Prepare and clean up test environment</description>

```ruby
class FeedbackTest < ActiveSupport::TestCase
  def setup
    @feedback = feedbacks(:one)
    @user = users(:alice)
  end

  test "feedback belongs to user" do
    assert_equal @user, @feedback.user
  end
end

```
</pattern>

---

## Minitest Assertions

<pattern name="common-assertions">
<description>Most frequently used Minitest assertions</description>

```ruby
class AssertionsTest < ActiveSupport::TestCase
  test "equality and boolean" do
    assert_equal 4, 2 + 2
    refute_equal 5, 2 + 2
    assert_nil nil
    refute_nil "something"
  end

  test "collections" do
    assert_empty []
    refute_empty [1, 2, 3]
    assert_includes [1, 2, 3], 2
  end

  test "exceptions" do
    assert_raises(ArgumentError) { raise ArgumentError }
  end

  test "difference" do
    assert_difference "Feedback.count", 1 do
      Feedback.create!(content: "Test feedback with minimum fifty characters", recipient_email: "[email protected]")
    end

    assert_no_difference "Feedback.count" do
      Feedback.new(content: nil).save
    end
  end

  test "match and instance" do
    assert_match /hello/, "hello world"
    assert_instance_of String, "hello"
    assert_respond_to "string", :upcase
  end
end

```
</pattern>

---

## Model Testing

### Testing Validations

<pattern name="presence-validations">
<description>Test required fields are validated</description>

```ruby
class FeedbackTest < ActiveSupport::TestCase
  test "valid with all required attributes" do
    feedback = Feedback.new(
      content: "This is constructive feedback that meets minimum length",
      recipient_email: "[email protected]"
    )
    assert feedback.valid?
  end

  test "invalid without content" do
    feedback = Feedback.new(recipient_email: "[email protected]")
    assert_not feedback.valid?
    assert_includes feedback.errors[:content], "can't be blank"
  end

  test "invalid without recipient_email" do
    feedback = Feedback.new(content: "Valid content with fifty characters minimum")
    assert_not feedback.valid?
    assert_includes feedback.errors[:recipient_email], "can't be blank"
  end
end

```
</pattern>

<pattern name="format-validations">
<description>Test format validations like email, URL, phone number</description>

```ruby
class FeedbackTest < ActiveSupport::TestCase
  test "invalid with malformed email" do
    invalid_emails = ["not-an-email", "@example.com", "user@", "user [email protected]"]

    invalid_emails.each do |invalid_email|
      feedback = Feedback.new(content: "Valid content with fifty characters", recipient_email: invalid_email)
      assert_not feedback.valid?, "#{invalid_email.inspect} should be invalid"
      assert_includes feedback.errors[:recipient_email], "is invalid"
    end
  end

  test "valid with edge case emails" do
    valid_emails = ["[email protected]", "[email protected]", "[email protected]"]

    valid_emails.each do |valid_email|
      feedback = Feedback.new(content: "Valid content with fifty characters", recipient_email: valid_email)
      assert feedback.valid?, "#{valid_email.inspect} should be valid"
    end
  end
end

```
</pattern>

<pattern name="length-validations">
<description>Test minimum and maximum length constraints</description>

```ruby
class FeedbackTest < ActiveSupport::TestCase
  test "invalid with content below minimum length" do
    feedback = Feedback.new(content: "Too short", recipient_email: "[email protected]")
    assert_not feedback.valid?
    assert_includes feedback.errors[:content], "is too short (minimum is 50 characters)"
  end

  test "valid at exactly minimum and maximum length" do
    assert Feedback.new(content: "a" * 50, recipient_email: "[email protected]").valid?
    assert Feedback.new(content: "a" * 5000, recipient_email: "[email protected]").valid?
  end

  test "invalid above maximum length" do
    feedback = Feedback.new(content: "a" * 5001, recipient_email: "[email protected]")
    assert_not feedback.valid?
    assert_includes feedback.errors[:content], "is too long (maximum is 5000 characters)"
  end
end

```
</pattern>

<pattern name="custom-validations">
<description>Test custom validation methods</description>

```ruby
# app/models/feedback.rb
class Feedback < ApplicationRecord
  validate :content_must_be_constructive

  private
  def content_must_be_constructive
    return if content.blank?
    offens
Files: 1
Size: 47.5 KB
Complexity: 32/100
Category: Code Review

Related in Code Review