如何使用冗余的`let!`方法调用来干扰RSpec测试?

时间:2018-02-08 21:01:56

标签: ruby-on-rails rspec dry

我在RSpec的Rails应用程序中如何使用这样的测试块:

describe "POST create" do
  describe "if we have a logged in user and he can be an owner" do
    describe "and if params are valid" do
      let!(:service_attributes_with_categories_1_and_2) {
        FactoryBot.attributes_for :service_with_categories_1_and_2
      }
      let!(:category_1) { FactoryBot.create :category, {id: 1} }
      let!(:category_2) { FactoryBot.create :category, {id: 2} }

      it "creates a new service" do
        # ...
      end
      it "creates associations with categories" do
        # ...
      end
    end
    describe "and if categories are not valid" do
      # ...
    end
    describe "and if some common service params are not valid" do
      # ...
    end
  end
  describe "if no user is logged in, but params are valid" do
    let!(:service_attributes_with_categories_1_and_2) {
      FactoryBot.attributes_for :service_with_categories_1_and_2
    }
    let!(:category_1) { FactoryBot.create :category, {id: 1} }
    let!(:category_2) { FactoryBot.create :category, {id: 2} }
    it "doesn't create a new service" do
      # ...
    end
    it "doesn't create associations with categories" do
      # ...
    end
  end
  describe "if logged user cannot be an owner, but params are valid" do
    let!(:service_attributes_with_categories_1_and_2) {
      FactoryBot.attributes_for :service_with_categories_1_and_2
    }
    let!(:category_1) { FactoryBot.create :category, {id: 1} }
    let!(:category_2) { FactoryBot.create :category, {id: 2} }

    it "doesn't create a new service" do
      # ...
    end
    it "doesn't create associations with categories" do
      # ...
    end
  end
end

正如我们所看到的,我有许多冗余的let!方法调用,但我不知道如何让它干涸。我不能只定义普通方法,因为在这种情况下,变量只能在此方法的范围内使用。我也不能让我的类别在一般范围内创建,因为在两种情况下,由于测试性质,不应该创建它们。那么,我应该从技术上做到这一点呢?

2 个答案:

答案 0 :(得分:1)

您可以将您的规格DRY设为:

1)使用let而不是定义方法。

2)安排context块,以便轻松容纳更多案件。

describe 'POST create' do
  let!(:service_attributes_with_categories_1_and_2) {
    FactoryBot.attributes_for :service_with_categories_1_and_2
  }
  let!(:category_1) { FactoryBot.build :category, {id: 1} }
  let!(:category_2) { FactoryBot.build :category, {id: 2} }
  let(:save_categories_1_and_2) { category_1.save && category_2.save }

  context 'when user is logged in' do
    context 'when user is an owner' do
      context 'when params are valid' do
        before do
          save_categories_1_and_2
        end

        it 'creates a new service' do
        end

        it 'creates associations with categories' do
        end
      end

      context 'when categories are not valid' do
      end

      context 'when some common service params are not valid' do
      end
    end

    context 'when user is not an owner' do
      context 'when params are valid' do
        before do
          save_categories_1_and_2
        end

        it "doesn't create a new service" do
        end

        it "doesn't create associations with categories" do
        end
      end
    end
  end

  context 'when no user is logged in' do
    context 'when params are valid' do
      before do
        save_categories_1_and_2
      end

      it "doesn't create a new service" do
      end

      it "doesn't create associations with categories" do
      end
    end
  end
end

答案 1 :(得分:0)

最后,我决定将FactoryBot.create函数拆分为两个步骤:FactoryBot.build.save函数在获取的对象上运行。它允许我将let!调用移动到主范围,并定义方法,该方法在我需要的情况下准确保存我的构建对象。我的DRY代码现在看起来像这样:

describe "POST create" do
  let!(:service_attributes_with_categories_1_and_2) {
    FactoryBot.attributes_for :service_with_categories_1_and_2
  }
  let!(:category_1) { FactoryBot.build :category, {id: 1} }
  let!(:category_2) { FactoryBot.build :category, {id: 2} }

  def save_categories_1_and_2
    category_1.save
    category_2.save
  end

  describe "if we have a logged in user and he can be an owner" do
    describe "and if params are valid" do
      before(:each) do
        save_categories_1_and_2
      end
      it "creates a new service" do
        # ...
      end
      it "creates associations with categories" do
        # ...
      end
    end
    describe "and if categories are not valid" do
      # ...
    end
    describe "and if some common service params are not valid" do
      # ...
    end
  end
  describe "if no user is logged in, but params are valid" do
    before(:each) do
      save_categories_1_and_2
    end
    it "doesn't create a new service" do
      # ...
    end
    it "doesn't create associations with categories" do
      # ...
    end
  end
  describe "if logged user cannot be an owner, but params are valid" do
    before(:each) do
      save_categories_1_and_2
    end
    it "doesn't create a new service" do
      # ...
    end
    it "doesn't create associations with categories" do
      # ...
    end
  end
end

非常感谢@midlins和@Taryn East指出我正确的方向。 @Taryn East - 您建议的调整也适用于那里描述的情况,但在我的应用程序中我还有更高级的案例,但这还不够。我认为这里提出的解决方案更具普遍性。