首页 > 解决方案 > 如何在使用 rspec 测试类中的任何方法之前初始化通用上下文变量

问题描述

我是 rspec 的新手。我正在为服务类编写测试,并且我希望在运行任何描述块之前初始化一堆实例。我做类似的事情:

RSpec.describe MyService do
before :each do

    let(:product){create(:product)}
    let(:article_student){ create(:article, targets: [:student], vat_type: vat_type, product: product)} #target student
    let(:article_teacher){ create(:article, targets: [:teacher], vat_type: vat_type, product: product) }#target teacher
    # creer des offres
    let(:offer){create(:offer, target: :student, license_length: :m12)}
    let(:offer_special_cyclic){create(:offer_special, cyclic_amount: true, free_article_id: article_teacher.id, minimum_amount: 10, license_length: :m12)} # free article @article_teacher
    let(:offer_special_non_cyclic){create(:offer_special, cyclic_amount: false, free_article_id: article_teacher.id, minimum_amount: 10, license_length: :m12)} # free article @article_teacher
    let(:order){ create(:order, establishment_account: establis

hment_account)}
end

然后我将有几个描述块(测试 MyService 中的不同方法,我希望在每个方法中修改我之前创建的变量:每个块。例如:

  describe "#create_from_order" do
    subject{ licenses }
    context "first time worker runs" do
      order.already_went_through_license_worker = false
      order.save!
      LicenseService.new.create_from_order(@order.id)
      let(:licenses){ License.where(order_id: @order.id)}
      specify { subject.count.to eq 34 }
      specify { subject.pluck(:is_from_offer_special).count(true).to eq 4}
      specify { subject.pluck(:is_from_offer_special).count(false).to eq 30 }
end
end

但是,当我尝试运行测试时,我发现order我的context块内部没有定义......

undefined local variable or method `order' for #<Class:0x007fc689ddfdb8>

我意识到我之前的:从未输入过每个代码。实现这一点的好方法是什么,那就是在测试类中的任何方法之前初始化一堆实例变量一般上下文。

标签: ruby-on-railsrubyrspecrspec-rails

解决方案


首先,将您的let陈述移出before块外。let被延迟评估,这意味着您定义的块 ( let(:foo) { block }) 仅在foo被调用时执行。但是通过将语句包装在一个before块中,您实际上并没有调用它们,因此不会发生任何事情。

要在每次测试之前执行语句,请使用let!. 这不会延迟评估,并且会在您定义语句后立即执行,这就是您在这种情况下想要的。

RSpec.describe MyService do
  let!(:product){create(:product)}
  let!(:article_student){ create(:article, targets: [:student], vat_type: vat_type, product: product)} #target student
  let!(:article_teacher){ create(:article, targets: [:teacher], vat_type: vat_type, product: product) } #target teacher
  # creer des offres
  let!(:offer){create(:offer, target: :student, license_length: :m12)}
  let!(:offer_special_cyclic){create(:offer_special, cyclic_amount: true, free_article_id: article_teacher.id, minimum_amount: 10, license_length: :m12)} # free article @article_teacher
  let!(:offer_special_non_cyclic){create(:offer_special, cyclic_amount: false, free_article_id: article_teacher.id, minimum_amount: 10, license_length: :m12)} # free article @article_teacher
  let!(:order){ create(:order, establishment_account: establishment_account)}
end

before现在,要修改对象,请在上下文中使用块。并确保你没有混合order@order

context "first time worker runs" do
  before do
    order.already_went_through_license_worker = false
    order.save!
    LicenseService.new.create_from_order(order.id)
  end

  let(:licenses){ License.where(order_id: order.id)}

  specify { subject.count.to eq 34 }
  specify { subject.pluck(:is_from_offer_special).count(true).to eq 4}
  specify { subject.pluck(:is_from_offer_special).count(false).to eq 30 }
end

那应该可以解决您的问题。


推荐阅读