RSpec: используйте `получать … точно … с … and_return …` с разными аргументами `with`

#ruby #rspec #rspec-mocks

#ruby #rspec #rspec-издевается

Вопрос:

Я пишу ожидание, которое проверяет, вызывался ли метод два раза с разными аргументами и возвращает ли он разные значения. На данный момент я просто записываю ожидание дважды:

 expect(ctx[:helpers]).to receive(:sanitize_strip).with(
  %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>},
  length: nil
).and_return('String description and newline')

expect(ctx[:helpers]).to receive(:sanitize_strip).with(
  %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>},
  length: 15
).and_return('String descr...')
  

Интересно, могу ли я использовать receive ... exactly ... with ... and_return ... вместо этого; что-то вроде:

 expect(ctx[:helpers]).to receive(:sanitize_strip).exactly(2).times.with(
  %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>},
  length: nil
).with(
  %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>},
  length: 15
).and_return('String descr...', 'String description and newline')
  

Приведенный выше код не работает, он выдает следующую ошибку:

 1) Types::Collection fields succeeds
   Failure/Error: context[:helpers].sanitize_strip(text, length: truncate_at)

     #<Double :helpers> received :sanitize_strip with unexpected arguments
       expected: ("Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>", {:length=>15})
            got: ("Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>", {:length=>nil})
     Diff:
     @@ -1,3  1,3 @@
      ["Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>",
     - {:length=>15}]
       {:length=>nil}]
  

Есть ли способ использовать receive ... exactly ... with ... and_return ... с разными with аргументами?

Комментарии:

1. Может быть, hash_including({}) вместо length: nil ? relishapp.com/rspec/rspec-mocks/docs/setting-constraints /…

Ответ №1:

Есть rspec-any_of драгоценный камень, который допускает следующий синтаксис путем добавления all_of средства сопоставления аргументов:

 expect(ctx[:helpers]).to receive(:sanitize_strip).with(
  %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>}
  all_of({length: 15}, {length: nil})
)
.and_return('String descr...', 'String description and newline')
.twice
  

Комментарии:

1. Потрясающе! И спасибо вам и вашим коллегам за драгоценный камень!

Ответ №2:

Не используя дополнительный gem, вы могли бы вызвать обычные переменные expect ... to receive ... with ... and_return ... with из each блока:

 describe '#sanitize_strip' do
  let(:html) do
    %{Stringn<a href="http://localhost:3000/">description</a> <br/>and newlinen<br>}
  end
  let(:test_data) do
    [
      [[html, length: nil], 'String description and newline'],
      [[html, length: 15], 'String descr...']
    ]
  end

  it 'returns sanitized strings stripped to the number of characters provided' do
    test_data.each do |args, result|
      expect(ctx[:helpers]).to receive(:sanitize_strip).with(*args).and_return(result)
    end

    # Trigger the calls to sanitize_strip
  end
end