简体   繁体   中英

RSpec has_many through #<ActiveRecord::Associations::CollectionProxy>

I have a has many through relationship model like this:

class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end

class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

I need to create a Physician which has many Patients, right? so on my test:

let!(:physician) { create(:physician) }
let!(:patients) { create_list(:patients, 2) }

and I did this:

before { physician.patients << patients }

I want to test the generated json with this

expect(physician.as_json).to eq({
  "id" => physician.id,
  "name" => physician.name,
  "patients" => physician.patients
})

I am expecting it would pass but it failed because of this #<ActiveRecord::Associations::CollectionProxy>

I checked using binding.pry by this:

physician.patients == patients and the result was true

Would you mind helping me, am I missing something here?

To link physician and patients just pass the key to create_list :

let!(:patients) { create_list(:patients, 2, physician: physician ) }

Or you can declare it as:

let(:physician) { create(:physician, patients: build_list(patients: 2)) }

But the test itself is still broken as mentioned by @TomLord. You need test the resulting hash - as including an association will cause it be converted to a serialized hash:

{
  "id" => 1,
  "name" => "Dr Suess",
  "patients" => [
     {
        "id" => 1,
        "name" => "The Cat in The Hat"
     },
     {
        "id" => 2,
        "name" => "The Grinch"
     },
  ]
}

Testing the exact output with eq is not optimal as each change to the serialization will break the test. Instead you can use the include matcher to specify what must be present:

describe '#as_json' do
   let!(:physician) { create(:physician) }
   let!(:patients) { create_list(:patients, 2) }
   subject(:serialized) { physician.as_json } 

   it { should include({
        "id" => physician.id,
        "name" => physician.name
   }) }
   it "includes the patients" do
      expect(serialized["patients"].length).to eq patients.length
      expect(serialized["patients"]).to include patients.first.as_json
      expect(serialized["patients"]).to include patients.last.as_json
   end
end

Unless you have overridden the as_json method this spec should fail as you need to explicitly include associations with physician.as_json(include: :patients) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM