mcpField
R
rspec

rspec

github.com/el-feo测试与质量1238 次安装14 周前更新
代码仓库

关于 rspec

面向 Ruby 和 Rails 项目的 RSpec 测试指导技能,供编写和维护测试的开发者使用。内容涵盖多种 spec 类型、测试替身、Factory Bot 及测试性能实践。

主要功能

  1. 01指导模型、请求和系统测试
  2. 02介绍 Factory Bot 测试数据策略
  3. 03说明匹配器、Mock 与 Stub
  4. 04提供 TDD 与测试分层建议
  5. 05涵盖 Rails 配置和性能实践

适用场景

  1. 01为 Rails 模型验证业务逻辑
  2. 02测试 API 请求、认证与响应
  3. 03通过浏览器验证用户工作流
  4. 04减少测试套件中的慢速用例

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/el-feo/ai-context --skill rspec

安装后在 Claude Code 中输入/rspec确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
best_practices.md
configuration.md
core_concepts.md
factory_bot.md
matchers.md
mocking.md
rails_testing.md

详细介绍

RSpec 测试技能

为 Ruby 和 Rails 应用编写全面 RSpec 测试的专家指南。本技能提供即时、可执行的测试策略,并附有针对复杂场景的深入参考资料。

快速入门

基本 RSpec 结构
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
  describe '#full_name' do
    it 'returns the first and last name' do
      user = User.new(first_name: 'John', last_name: 'Doe')
      expect(user.full_name).to eq('John Doe')
    end
  end
end

核心概念:

  • describe:对相关测试(类、方法)进行分组
  • context:描述特定场景
  • it:单个测试示例
  • expect:使用匹配器进行断言
运行测试
# Run all specs
bundle exec rspec

# Run specific file
bundle exec rspec spec/models/user_spec.rb

# Run specific line
bundle exec rspec spec/models/user_spec.rb:12

# Run with documentation format
bundle exec rspec --format documentation

# Run only failures from last run
bundle exec rspec --only-failures

核心测试模式

1. 模型规格测试

测试业务逻辑、验证、关联和方法:

RSpec.describe Article, type: :model do
  # Test validations
  describe 'validations' do
    it { should validate_presence_of(:title) }
    it { should validate_length_of(:title).is_at_most(100) }
  end

  # Test associations
  describe 'associations' do
    it { should belong_to(:author) }
    it { should have_many(:comments) }
  end

  # Test instance methods
  describe '#published?' do
    context 'when publish_date is in the past' do
      it 'returns true' do
        article = Article.new(publish_date: 1.day.ago)
        expect(article.published?).to be true
      end
    end

    context 'when publish_date is in the future' do
      it 'returns false' do
        article = Article.new(publish_date: 1.day.from_now)
        expect(article.published?).to be false
      end
    end
  end

  # Test scopes
  describe '.recent' do
    it 'returns articles from the last 30 days' do
      old = create(:article, created_at: 31.days.ago)
      recent = create(:article, created_at: 1.day.ago)

      expect(Article.recent).to include(recent)
      expect(Article.recent).not_to include(old)
    end
  end
end
2. 请求规格测试

测试整个技术栈中的 HTTP 请求和响应:

RSpec.describe 'Articles API', type: :request do
  describe 'GET /articles' do
    it 'returns all articles' do
      create_list(:article, 3)

      get '/articles'

      expect(response).to have_http_status(:success)
      expect(JSON.parse(response.body).size).to eq(3)
    end
  end

  describe 'POST /articles' do
    context 'with valid params' do
      it 'creates a new article' do
        article_params = { article: { title: 'New Article', body: 'Content' } }

        expect {
          post '/articles', params: article_params
        }.to change(Article, :count).by(1)

        expect(response).to have_http_status(:created)
      end
    end

    context 'with invalid params' do
      it 'returns errors' do
        invalid_params = { article: { title: '' } }

        post '/articles', params: invalid_params

        expect(response).to have_http_status(:unprocessable_entity)
      end
    end
  end

  describe 'authentication' do
    it 'requires authentication for create' do
      post '/articles', params: { article: { title: 'Test' } }

      expect(response).to have_http_status(:unauthorized)
    end

    it 'allows authenticated users to create' do
      user = create(:user)

      post '/articles',
        params: { article: { title: 'Test' } },
        headers: { 'Authorization' => "Bearer #{user.token}" }

      expect(response).to have_http_status(:created)
    end
  end
end
3. 系统规格测试(端到端)

使用 Capybara 在浏览器中测试用户工作流:

RSpec.describe 'Article management', type: :system do
  before { driven_by(:selenium_chrome_headless) }

  scenario 'user creates an article' do
    visit new_article_path

    fill_in 'Title', with: 'My Article'
    fill_in 'Body', with: 'Article content'
    click_button 'Create Article'

    expect(page).to have_content('Article was successfully created')
    expect(page).to have_content('My Article')
  end

  scenario 'user edits an article' do
    article = create(:article, title: 'Original Title')

    visit article_path(article)
    click_link 'Edit'

    fill_in 'Title', with: 'Updated Title'
    click_button 'Update Article'

    expect(page).to have_content('Updated Title')
    expect(page).not_to have_content('Original Title')
  end

  # Test JavaScript interactions
  scenario 'user filters articles', js: true do
    create(:article, title: 'Ruby Article', category: 'ruby')
    create(:article, title: 'Python Article', category: 'python')

    visit articles_path

    select 'Ruby', from: 'filter'

    expect(page).to have_content('Ruby Article')
    expect(page).not_to have_content('Python Article')
  end
end

Factory Bot 集成

使用 FactoryBot 创建测试数据。在 spec/factories/ 中定义工厂,使用 traits 创建不同变体;如果不需要持久化,优先使用 build,而非 create。

有关工厂定义、traits、序列、关联和构建策略,请参阅 references/factory_bot.md。

必备匹配器

类别示例
相等性eq、eql、be、equal
真值be_truthy、be_falsy、be_nil、be_a
集合include、contain_exactly、match_array
变化change、raise_error、have_enqueued_job

完整的匹配器参考请参阅 references/matchers.md。

Mock、Stub 和 Double

使用 double 或 instance_double(优先使用,可针对真实类进行验证)创建测试替身。使用 allow(obj).to receive(:method) 设置 Stub,使用 expect(obj).to receive(:method) 设置预期,并通过 have_received 间谍事后验证。

有关测试替身、Stub、消息预期和间谍,请参阅 references/mocking.md。

DRY 测试技巧

使用 let(惰性)和 let!(即时)创建测试数据,使用 before 钩子共享设置,使用 shared_examples 复用测试组,并使用 shared_context 复用设置块。对于被测对象,优先使用 subject。

有关钩子、let、共享示例、共享上下文和 subject 的详细说明,请参阅 references/core_concepts.md。

TDD 工作流

Red-Green-Refactor 循环
  1. Red:先编写一个失败的测试
describe User do
  it 'has a full name' do
    user = User.new(first_name: 'John', last_name: 'Doe')
    expect(user.full_name).to eq('John Doe')
  end
end
# Fails: undefined method `full_name'
  1. Green:编写最少的代码使测试通过
class User
  def full_name
    "#{first_name} #{last_name}"
  end
end
# Passes!
  1. Refactor:在保持测试通过的同时改进代码
测试策略

面向用户的功能应从系统规格测试开始:

  • 测试完整工作流
  • 信心最高
  • 运行最慢

对于 API/控制器逻辑,转向请求规格测试:

  • 测试 HTTP 交互
  • 比系统规格测试快
  • 覆盖身份验证、授权和边缘情况

对于业务逻辑,使用模型规格测试:

  • 测试计算、验证和作用域
  • 快速且聚焦
  • 占测试套件的大部分

配置最佳实践

spec/rails_helper.rb
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
abort("Run in production!") if Rails.env.production?
require 'rspec/rails'

# Auto-require support files
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

RSpec.configure do |config|
  # Use transactional fixtures
  config.use_transactional_fixtures = true

  # Infer spec type from file location
  config.infer_spec_type_from_file_location!

  # Filter Rails backtrace
  config.filter_rails_from_backtrace!

  # Include FactoryBot methods
  config.include FactoryBot::Syntax::Methods

  # Include request helpers
  config.include RequestHelpers, type: :request

  # Capybara configuration for system specs
  config.before(:each, type: :system) do
    driven_by :selenium_chrome_headless
  end
end
spec/spec_helper.rb
RSpec.configure do |config|
  # Show detailed failure messages
  config.example_status_persistence_file_path = "spec/examples.txt"

  # Disable monkey patching (use expect syntax only)
  config.disable_monkey_patching!

  # Output warnings
  config.warnings = true

  # Profile slowest tests
  config.profile_examples = 10 if ENV['PROFILE']

  # Run specs in random order
  config.order = :random
  Kernel.srand config.seed
end

常见模式

有关后台作业、邮件程序、文件上传、分页和搜索的测试,请参阅 references/rails_testing.md。

性能提示

  1. 使用 let 代替 before,实现惰性加载

  2. 测试逻辑时避免数据库调用(使用 Mock)

  3. 如果不需要持久化,使用 build 代替 create

  4. 对于带关联的非持久化对象,使用 build_stubbed

  5. 为慢速测试添加标签,并在开发期间排除这些测试:

    it 'slow test', :slow do
      # test code
    end
    
    # Run with: rspec --tag ~slow
    

何时使用各类 spec

  • Model spec:业务逻辑、计算、验证、作用域
  • Request spec:API 端点、身份验证、授权、JSON 响应
  • System spec:用户工作流、JavaScript 交互、表单提交
  • Mailer spec:邮件内容、收件人、附件
  • Job spec:后台任务的入队和执行
  • Helper spec:视图辅助方法
  • Routing spec:自定义路由(通常不需要)

快速参考

最常用的命令:

rspec                          # Run all specs
rspec spec/models              # Run model specs
rspec --tag ~slow              # Exclude slow specs
rspec --only-failures          # Rerun failures
rspec --format documentation   # Readable output
rspec --profile               # Show slowest specs

最常用的匹配器:

  • eq(expected) - 值相等
  • be_truthy / be_falsy - 真值判断
  • include(item) - 集合成员判断
  • raise_error(Error) - 异常
  • change { }.by(n) - 状态变化

最常用的桩:

  • allow(obj).to receive(:method) - 为方法设置桩
  • expect(obj).to receive(:method) - 预期方法调用
  • double('name', method: value) - 创建测试替身

参考文档

有关特定主题的详细信息,请参阅 references 目录:

常见场景

有关调试失败的测试、测试复杂查询和测试回调的信息,请参阅 references/rails_testing.md。

评论

支持 Markdown