You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改写DRY的RSpec用例以消除隐式块预期弃用警告

解决RSpec隐式块弃用警告并保持代码DRY

问题背景

在将项目从Rails 5迁移至Rails 6(当前环境:Rails 6.0.6.1、RSpec 3.12.0、rspec-rails 5.1.2)时,编写的RSpec代码触发了隐式块预期语法已弃用,应向expect传递块而非参数的警告。

原代码如下:

describe "Create with a classification class added" do
  before do
    @lambda_block = lambda {
      click_button I18n.t(:button_save)
      expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
    }     
  end   

  describe "Save should create a ClassificationScheme and a ClassificationSchemesClass" do
    subject { @lambda_block }
    it { is_expected.to change(ClassificationScheme, :count).by(1) }
    it { is_expected.to change(ClassificationSchemesClass, :count).by(1) }
  end   
end   

修改为无警告版本后,代码出现重复,违反DRY原则:

describe "Create with a classification class added" do 
  it "Save should create a ClassificationScheme" do
    expect{
      click_button I18n.t(:button_save)
      expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
    }.to change{ClassificationScheme.count}.by(1)
  end

  it "Save should create a ClassificationSchemesClass" do
    expect{
      click_button I18n.t(:button_save)
      expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
    }.to change{ClassificationSchemesClass.count}.by(1)
  end
end

尝试直接传递@lambda_block时,expect{@lambda_block}不会执行lambda块,expect(@lambda_block)虽能通过测试但仍触发警告。

解决方案

要同时满足消除警告和保持DRY的需求,可以通过以下几种方式实现:

方法一:封装辅助方法

把重复的操作封装成一个方法,在expect块中调用该方法:

describe "Create with a classification class added" do 
  def perform_save_and_verify_success
    click_button I18n.t(:button_save)
    expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
  end

  it "creates a ClassificationScheme" do
    expect { perform_save_and_verify_success }.to change(ClassificationScheme, :count).by(1)
  end

  it "creates a ClassificationSchemesClass" do
    expect { perform_save_and_verify_success }.to change(ClassificationSchemesClass, :count).by(1)
  end
end

方法二:调用lambda的call方法

如果坚持使用lambda,可以在expect块中调用lambda的call方法,同时改用let定义lambda(符合RSpec最佳实践):

describe "Create with a classification class added" do
  let(:save_and_verify) do
    lambda {
      click_button I18n.t(:button_save)
      expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
    }
  end

  it "creates a ClassificationScheme" do
    expect { save_and_verify.call }.to change(ClassificationScheme, :count).by(1)
  end

  it "creates a ClassificationSchemesClass" do
    expect { save_and_verify.call }.to change(ClassificationSchemesClass, :count).by(1)
  end
end

方法三:使用共享示例组

如果这类测试逻辑需要在多个地方复用,可以定义共享示例组:

RSpec.shared_examples "creates a record" do |model_class|
  it "creates a #{model_class.name}" do
    expect {
      click_button I18n.t(:button_save)
      expect(page).to have_text(I18n.t :'flash.classification_scheme.create_success')
    }.to change(model_class, :count).by(1)
  end
end

describe "Create with a classification class added" do 
  include_examples "creates a record", ClassificationScheme
  include_examples "creates a record", ClassificationSchemesClass
end

说明

RSpec 3.12+弃用了将lambda作为参数传递给expect的语法,要求必须传递可执行的块。上述方法均遵循该要求,同时避免了代码重复。

内容的提问来源于stack exchange,提问作者Tibor Nagy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:05:09