Rspec: проверьте, включает ли массив объект, который включает свойство

У меня есть массив json, полный объектов.

my_array = [{id => 6, name => "bob"}, 
           {id => 5, name => "jim"}, 
           {id => 2, name => "steve"}]

Мне нужно выяснить, содержит ли массив объект, который содержит атрибут "id", который установлен в 5. Атрибут "name" неизвестен.

Как это сделать в rspec?

Я знаю, если бы у меня был атрибут имени, который я знаю, я мог бы просто сделать:

my_array.should include({:id => 5, :name => "jim"}) 

Ответ 1

expect(myArray.find { |item| item[:id] == 5 }).to_not be_nil

или с синтаксисом устаревшего синтаксиса

myArray.find { |item| item[:id] == 5 }.should_not be_nil

Обратите внимание, что myArray не соответствует правилам Ruby. Переменные используют подчеркивание

my_array

не camelcase

myArray

Ответ 2

Это стоило бы того, если бы вы делали многие из них, но вы могли бы определить пользовательский макет:

RSpec::Matchers.define :object_with_id do |expected|
  match do |actual|
    actual[:id] == expected
  end
  description do
    "an object with id '#{expected}'"
  end
end

# ...

myArray.should include(object_with_id 5)

Ответ 3

Поместите этот макет any в spec/support/matchers.rb и потребуйте его в spec_helper.rb

RSpec::Matchers.define :any do |matcher|
  match do |actual|
    actual.any? do |item|
      matcher.matches?(item)
    end
  end
end

Затем вы можете использовать его в следующих примерах:

expect(my_array).to any(include(id: 5))

Ответ 4

Здесь пользовательский соединитель "include_object" (возможно, лучшее имя должно использоваться, поскольку оно просто проверяет наличие идентификатора)

используется следующим образом

obj = {id:1}
objs = [{id: 1}, {id: 2}, {id: 3}]
expect(objs).to include_object obj

Матчи могут обрабатывать объекты, хеши (символы или строки) Он также выводит только идентификатор в массиве на исключение для более удобного просмотра

RSpec::Matchers.define :include_object do |expected|
  ids = []
  match do |actual|
    ids = actual.collect { |item| item['id'] || item[:id] || item.id }

    ids.find { |id| id.to_s == expected.id.to_s }
  end

  failure_message_for_should_not do |actual|
    "expected that array with object id #{ids} would contain the object with id '#{expected.id}'"
  end

  failure_message_for_should_not do |actual|
    "expected that array with object id #{ids} would not contain the object with id '#{expected.id}'"
  end
end

Ответ 5

Вы можете развернуть массив и проверить соответствие двух массивов, как здесь:

expect(my_array).to include(*compare_array) 

Он разворачивается и сопоставляет каждое значение массива.

Это эквивалентно этому:

expected([1, 3, 7]).to include(1,3,7)

Источник: Дополнительная документация

Ответ 6

Я бы использовал RSpec 3 с составным include следующим образом:

expect(my_array).to include(include(id: 5))

Это может привести к более подробному выводу через RSpec в случае сбоя.

it 'expects to have element with id 3' do
  my_array = [
    { id: 6, name: "bob" },
    { id: 5, name: "jim" },
    { id: 2, name: "steve" }
  ]
  expect(my_array).to include(include(id: 3))
end

Это приведет к появлению следующего сообщения об ошибке:

Failures:

  1) Test expects to have element with id
     Failure/Error: expect(my_array).to include(include(id: 3))

       expected [{:id => 6, :name => "bob"}, {:id => 5, :name => "jim"}, {:id => 2, :name => "steve"}] to include (include {:id => 3})
       Diff:
       @@ -1,2 +1,2 @@
       -[(include {:id => 3})]
       +[{:id=>6, :name=>"bob"}, {:id=>5, :name=>"jim"}, {:id=>2, :name=>"steve"}]

Дальнейшее чтение:

https://relishapp.com/rspec/rspec-expectations/docs/composing-matchers

Ответ 7

Это также возможно, используя псевдоним have_attributes:

expect(my_array).to include( an_object_having_attributes(id: 5) )

или, как в моем собственном случае использования, сопоставляя весь массив:

expect(my_array).to contain_exactly(
  an_object_having_attributes(id: 5),
  an_object_having_attributes(id: 6),
  an_object_having_attributes(id: 2)
)