Передача параметров в общий пример rspec

у меня есть процесс, который возвращает результирующий набор, который я хочу проверить на валидность с помощью rspec. Процесс вернет различные результаты на основе параметров, но есть много примеров, которые являются общими для всех из них, поэтому я хотел бы создать набор общих примеров, которые я могу запустить против всех из них.

Я знаю, что предпочтительной практикой является использование в результате. Проблема в том, что каждый процесс занимает минуту или две, чтобы генерировать результат и у меня, наверное, 30 образцы. Со всеми перестановками, основанными на разных параметрах, я запускаю около 500 примеров.Если бы мне пришлось перестраивать результат для каждого примера, тест выполнялся бы больше дня.

поэтому вместо этого я создаю результат в блоке before(:all) и назначаю его атрибуту примерно так:

RSpec.describe 'Test Description' do
  attr_reader :result

  before(:all)
    @result = build_result({some_parameters})
  end

  context 'Some context' do
    it 'Looks lik a result' do 
      expect(result.something).to ...
    end

    it 'Feels lik a result' do 
      expect(result.something).to ...
    end
  end
end

возможно, есть лучший способ, чем использование атрибута. Я хочу сделать что-то вроде этого:

RSpec.describe 'Test Description' do
  attr_reader :result

  before(:all)
    @result = build_result({some_parameters})
  end

  context 'Some context' do
    it_behaves_like "A result" result
  end
end

С помощью атрибута в этом контексте не удается. Есть ли другой способ сделать это?

3 ответов


вы можете сгруппировать все ваши утверждения по результату в один пример. Таким образом, let оценивается только один раз.

RSpec.describe 'Test Description' do
  context 'for params x and y' do
    let(:expected_x) { 'x' }
    let(:expected_y) { 'y' }

    subject { build_result({x: 'x', y: 'y'}) }

    specify :aggregate_failures do
      expect(subject.x).to eq(expected_x)
      expect(subject.y).to eq(expected_y)
    end
  end
end

это идет вразрез с руководством "один тест, одно утверждение", но если операция очень дорогостоящая, я думаю, что это разумный подход. С :aggregate_failures вы получите отдельные неудачи для каждого утверждения, поэтому вы не пропустите это.


вы можете передавать аргументы в общие примеры, такие как:

shared_examples_for "A result" do |argument|
 # some tests with argument
end

и затем пройти в my_argument такой:

it_behaves_like "A result", my_argument

можно использовать let.

let(:result)     { build_result({some_parameters}) }

Это создает переменную экземпляра, который вы можете использовать в вашем тесте.

согласно документации на let,

когда вам нужно назначить переменную вместо использования блока before создайте переменную экземпляра, используйте let. Использование Let переменной lazy нагрузки только тогда, когда он используется в первый раз в тесте и кэшируется пока этот конкретный тест не будет завершен.

плохо

describe '#type_id' do
  before { @resource = FactoryGirl.create :device }
  before { @type     = Type.find @resource.type_id }

  it 'sets the type_id field' do
    expect(@resource.type_id).to equal(@type.id)
  end
end

хороший

describe '#type_id' do
  let(:resource) { FactoryGirl.create :device }
  let(:type)     { Type.find resource.type_id }

  it 'sets the type_id field' do
    expect(resource.type_id).to equal(type.id)
  end
end