ruby Rspec + shared_examples,如何避免重复创建对象?

bbmckpt7  于 2023-06-29  发布在  Ruby
关注(0)|答案(1)|浏览(78)

我在我的Rspec中剪切了以下代码。
我想只创建一次供应商,但相反,我看到为每个测试创建了不同的供应商对象(总共3次)。我在实际实现中通过binding.pry验证了这一点。
1.有人能解释一下为什么会这样吗?
1.如何避免这一点?
我不能使用build而不是create,因为我需要在实现中与供应商关联的id。
我使用共享的示例,因为我有50多个产品要编写相同的测试。

let!(:supplier) { create(:supplier) }

shared_examples 'a product not in stock with supplier' do |product|
  it 'raises an error' do
    expect { described_class.dispatch_product!(supplier, product) }.to raise_error 'Product is not in stock'
  end
end

context 'when the state is not supported' do
  it_behaves_like 'a product not in stock with supplier', Chair_1
  it_behaves_like 'a product not in stock with supplier', Table_1
  it_behaves_like 'a product not in stock with supplier', Sofa_1
end
yqyhoc1h

yqyhoc1h1#

这是使用letlet!时的预期行为。它的值将被存储为一个期望值(一个it块)。
如果你想避免为每个测试重新创建supplier,那么我看到了两个选项:
1.把所有的期望合二为一

let(:supplier) { create(:supplier) }

shared_examples 'a product not in stock with supplier' do |products|
  it 'raises an error' do
    products.each do |product|
      expect { 
        described_class.dispatch_product!(supplier, product) 
      }.to raise_error 'Product is not in stock'
    end
  end
end

context 'when the state is not supported' do
  it_behaves_like 'a product not in stock with supplier', 
                  [Chair_1, Table_1, Sofa_1]
end

1.您将示例存储在示例变量中并手动处理它

shared_examples 'a product not in stock with supplier' do |product|
  it 'raises an error' do
    expect { 
      described_class.dispatch_product!(supplier, product) 
    }.to raise_error 'Product is not in stock'
  end
end

context 'when the state is not supported' do
  before(:context) { @supplier ||= create(:supplier) }

  it_behaves_like 'a product not in stock with supplier', 
                  Chair_1 { let(:supplier) { @supplier } }
  it_behaves_like 'a product not in stock with supplier', 
                  Table_1 { let(:supplier) { @supplier } }
  it_behaves_like 'a product not in stock with supplier', 
                  Sofa_1 { let(:supplier) { @supplier } }
end

我不喜欢这些选项中的任何一个,我认为它们使规范更难阅读,并且是RSpec默认行为和设计原则的丑陋解决方案。
在这种情况下,除了在工厂中使用Fixtures之外,您可能还需要考虑在测试中使用Fixtures。如果您只为一个供应商创建了一个默认的fixture,那么您可以为所有类型的测试重用同一个供应商,在这些测试中,您不关心使用具有任何特定配置的供应商。
如果你在应用中配置了一个fixture,并且有一个有效的suppliers(:default) fixture,那么它可能看起来像这样:

let(:supplier) { suppliers(:default) } # would reuse the same instance from the db

shared_examples 'a product not in stock with supplier' do |product|
  it 'raises an error' do
    expect { 
      described_class.dispatch_product!(supplier, product) 
    }.to raise_error 'Product is not in stock'
  end
end

context 'when the state is not supported' do
  it_behaves_like 'a product not in stock with supplier', Chair_1
  it_behaves_like 'a product not in stock with supplier', Table_1
  it_behaves_like 'a product not in stock with supplier', Sofa_1
end

相关问题