Home > Back-end >  rspec: test array of instances
rspec: test array of instances

Time:09-28

I'm trying to create rspec tests to test an array of instances. Specifically, I want to verify certain attributes of each instance within the array. Is there a way to use rspec to test this scenario?

For example, suppose I have the following array that I want to verify:

[#<Car id:1, buy_date: "2022-10-10", model: "Ford">, 
 #<Car id:2, buy_date: "2021-01-10", model: "Ferrari">, 
 #<Car id:3, buy_date: "2022-03-12", model: "Toyota">]

As my test, I want to check that the buy_date is correct. I tried the following expect statement but I don't think it's meant for arrays of instances so the tests failed when I expected them to pass.

expect(cars).to include([
                have_attributes(
                    buy_date: "2022-10-10"
                ),
                have_attributes(
                    buy_date: "2021-01-10"                   
                ),
                have_attributes(
                    buy_date: "2022-03-12"
                )
            ])

I've also tried it with match_array instead of include but the result was the same.

Any ideas how to use rspec to accomplish this?

CodePudding user response:

You could put your expected values in an array, then reference it from a loop over your testable data.

For example:

expected = ["2022-10-10", "2021-01-10", "2022-03-12"]
cars.each_with_index do |car, index|
  expect(car[:buy_date]).to eql(expected[index]))
end

CodePudding user response:

The include matcher is meant to check if the tested collection contains the passed value. So you are checking if any of the elements in the car array is an array, containing 3 objects with the given attributes.

That's because you can have an array of arrays, and you should be able to test if a given array is contained as a value in the array of arrays.

To test multiple values, you have to pass them as multiple arguments:

expect(cars).to include(
                have_attributes(
                    buy_date: "2022-10-10"
                ),
                have_attributes(
                    buy_date: "2021-01-10"                   
                ),
                have_attributes(
                    buy_date: "2022-03-12"
                )
            )

To improve readability, RSpec offers multiple aliases:

expect(cars).to include(
                an_object_having_attributes(
                    buy_date: "2022-10-10"
                ),
                an_object_having_attributes(
                    buy_date: "2021-01-10"                   
                ),
                an_object_having_attributes(
                    buy_date: "2022-03-12"
                )
            )

Now the difference whether you will use include in this way, or match_array is whether you want to be permissive, e.g. allow cars to contains other elements as well, or you want it to contain exactly those ones.

Also, in terms of readability, I'd rather combine it with multiple matchers:

expect(cars).to
   include(an_object_having_attributes(buy_date: "2022-10-10"))
  .and include(an_object_having_attributes(buy_date: "2021-01-10"))
  .and include(an_object_having_attributes(buy_date: "2022-03-12"))
  • Related