Ruby on rails 如何使用冗余的'let!'干涸RSpec测试方法调用?

Ruby on rails 如何使用冗余的'let!'干涸RSpec测试方法调用?,ruby-on-rails,rspec,dry,Ruby On Rails,Rspec,Dry,我如何在我的Rails应用程序中使用RSpec创建这样的测试块: 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.attri

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

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方法调用,但我不知道如何使其干燥。我不能只定义普通方法,因为在这种情况下,变量只在这个方法的作用域中可用。我也不能让我的类别在一般范围内创建,因为在两种情况下,由于测试的性质,它们不应该被创建。那么,从技术上讲,我应该如何做到这一点呢?

最后我决定将
FactoryBot.create
函数分为两个步骤:
FactoryBot.build
save
函数在获取的对象上运行。它允许我移动我的
let调用main作用域和define方法,该方法在需要的情况下准确地保存构建的对象。我的干式代码现在看起来是这样的:

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-您建议的调整也适用于上述案例,但在我的应用程序中,我还有更高级的案例,这还不够。我认为这里介绍的解决方案更具普遍性。

您可以按如下方式使您的规格干燥:

1) 使用
let
代替定义方法

2) 将您的
上下文
块安排为便于适应进一步的情况

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

是否有任何理由说明在
描述“后期创建”do
下的顶部范围内声明一次它们将不起作用?是的,这是因为在某些情况下,我不希望在数据库中创建类别。是否还有其他测试没有显示?根据您展示的内容,您有三个测试,每个测试设置相同的测试数据。你为什么不在做出断言之前设置一次呢?你错了。第一组
let调用在作用域
“中定义,如果参数有效”
,而在作用域
“中我们没有定义,如果类别无效”
“如果一些公共服务参数无效”
。调整作用域,使前两个作用域是“如果参数有效”(在这里声明这些
与“If参数无效”(如果参数无效),所有其他作用域都在这些范围内。