Search code examples
ruby-on-railsjsonrubyrspecactive-model-serializers

RSpec equality matcher failing for serializer test


I am writing a test for one of my Active Model Serializers to make sure that the JSON output is what I expect. However, I cannot figure out why RSpec is parsing my 'expected' output to leave out my array of testjobs, and I do not understand why I cannot get 'expected' and 'got' outputs to equal each other. At one point, I even copy-pasted the 'got' result to my 'expected' input and still received a failure message that the two strings were not equal. However, when I compared those two strings in REPL using ==, the output was true. How do I resolve these issues to get an effective test?

RSpec Error:

Failures:

  1) TestrunSerializer creates special JSON for the API
     Failure/Error: expect(serializer.to_json).to eq('{"testrun":{"id":1,"run_at":null,"started_at":null,"state":"pending","completed_at":null,"testjobs":[{"id":2,"active":false,"testchunk_id":2,"testrun_id":1,"testchunk_name":"flair","testchunk":{"id":15,"name":"flair"}}],"branch":{"id":1,"name":"dev","repository":{"id":321,"url":"fakeurl.com"}}}}')

       expected: "{\"testrun\":{\"id\":1,\"run_at\":null,\"started_at\":null,\"state\":\"pending\",\"completed_at\":nu...r\"}}],\"branch\":{\"id\":1,\"name\":\"dev\",\"repository\":{\"id\":321,\"url\":\"fakeurl.com\"}}}}"
            got: "{\"testrun\":{\"id\":1,\"run_at\":null,\"started_at\":null,\"state\":\"pending\",\"completed_at\":nu...s\":[],\"branch\":{\"id\":1,\"name\":\"dev\",\"repository\":{\"id\":321,\"url\":\"fakeurl.com\"}}}}"

       (compared using ==)
     # ./spec/serializers/testrun_spec.rb:11:in `block (2 levels) in <top (required)>'

Finished in 0.79448 seconds (files took 5.63 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/serializers/testrun_spec.rb:8 # TestrunSerializer creates special JSON for the API

Here is the RSpec test:

require 'rails_helper'

describe TestrunSerializer, type: :serializer do
  let(:repo)      { Repository.create(id: 321, url: "fakeurl.com") }
  let(:branch)    { Branch.create(id: 1,name: "dev", repository_id: repo.id) }
  let(:testchunk) { Testchunk.create(id: 15, name: "flair") }

  it "creates special JSON for the API" do
    serializer = TestrunSerializer.new Testrun.new("id":1, name: "name", "run_at": nil, state: "pending", branch_id: branch.id)
    testjob = Testjob.create(id: 8, active: false, testchunk_id: testchunk.id, testrun_id: 1)
    expect(serializer.to_json).to eq('{"testrun":{"id":1,"run_at":null,"started_at":null,"state":"pending","completed_at":null,"testjobs":[{"id":2,"active":false,"testchunk_id":2,"testrun_id":1,"testchunk_name":"flair","testchunk":{"id":15,"name":"flair"}}],"branch":{"id":1,"name":"dev","repository":{"id":321,"url":"fakeurl.com"}}}}')
  end
end

Here is the actual serializer:

class TestrunSerializer < ActiveModel::Serializer
  attributes :id, :run_at, :started_at, :state, :completed_at, :testjobs
  has_many :testjobs
  has_one :branch
end 

Technologies used: Rails 5.1, RSpec 3.6, Ruby 2.4


Solution

  • The reason why your test didn't pass is trivial: inside the it block, you assigned the Testrun id (1) while creating the Testjob record, but the Testrun record does not exist.

    SomeActiveRecord.new() will not create any actual record until you invoke save() on it, or you can just invoke SomeActiveRecord.create for that.

    some_active_record = SomeActiveRecord.new(...)
    some_active_record.save
    
    # or
    some_active_record = SomeActiveRecord.create(...)
    

    So the final solution may look something like:

    it "creates special JSON for the API" do
      testrun = Testrun.create(id: 1, name: "name", run_at: nil, state: "pending", branch_id: branch.id)
      serializer = TestrunSerializer.new(testrun)
      testjob = Testjob.create(id: 8, active: false, testchunk_id: testchunk.id, testrun_id: testrun.id)
      expect(serializer.to_json).to eq('{"testrun":{"id":1,"run_at":null,"started_at":null,"state":"pending","completed_at":null,"testjobs":[{"id":2,"active":false,"testchunk_id":2,"testrun_id":1,"testchunk_name":"flair","testchunk":{"id":15,"name":"flair"}}],"branch":{"id":1,"name":"dev","repository":{"id":321,"url":"fakeurl.com"}}}}')
    end
    

    Improvement Scope:

    Please have a look at the tests for :json adapter in the active_model_serializers repo: https://github.com/rails-api/active_model_serializers/blob/v0.10.6/test/action_controller/json/include_test.rb.

    You can easily convert the tests to suite with rspec.

    If you want to test the json output, then you should put the tests under controller or request specs; rather than in serializers. Because rendering json is the responsibility of the adapter; serializers merely feed the adapter with all the attributes and associations defined in them.