Jsonapi spec helpers

View the Project on GitHub

jsonapi_spec_helpers

This gem provides a number of low-level helpers as well as an abstraction for asserting entire payloads.

Low-level helpers

assert_payload

In JSONAPI responses, the same object payload can be repeated across many different responses. So, instead of asserting on the response itself, you can assert the response contains a given payload. Start by defining your payloads:

JsonapiSpecHelpers::Payload.register(:person) do
  key(:name)
  key(:age)
end

And then assert on them in specs:

person = Person.last
# Pass:
# * registered payload name
# * a record to compare against
# * a json_item
assert_payload(:person, person, json_include('people'))

This asserts:

It’s the rough code equivalent of:

person = Person.last
json = JSON.parse(response.body)
included = json['included'].find { |incl| incl['type'] == 'people' }
expect(included).to have_key('name')
expect(included['name']).to eq(person.name)
expect(included).to have_key('age')
expect(included['age']).to eq(person.age)
expect(included.keys).to match_array(%w(name age))

Custom Payloads

Let’s say your serializer always capitalizes name. In this case, the above assert_record_payload would fail. To make it pass, supply a block to the key argument:

JsonapiSpecHelpers::Payload.register(:person) do
  key(:name) { |record| record.name.upcase }
  key(:age)
end

The test will now pass, as we’ve registered a custom response comparison.

You can customize payloads at assertion-time as well. Let’s say you only want to render age if the current user is an admin. A spec could look something like:

sign_in(:admin)
get :show, id: person.id
assert_payload(:person, person, json_item)
sign_in(:non_admin)
get :show, id: person.id
assert_payload(:person, person, json_item) do
  no_key(:age)
end

The no_key method overrides the default age key and specifies this key should not be present in the response. Any keys specified in this block will override the defaults.

Request helpers

It’s recommended to use jsonapi_get, jsonapi_post, etc instead of the corresponding rspec methods. This way we ensure jsonapi_headers are always passed, and can be overridden. Let’s say we want to add a JWT to our request headers:

def jsonapi_headers
  headers = super
  headers['X-JWT'] = jwt
  headers
end

let(:jwt) { "s0m3t0k3n" }

let(:payload) do
  {
    data: {
      type: 'employees',
      attributes: { name: 'John Doe' }
    }
  }
end

it "works correctly" do
  jsonapi_post("/api/v1/employees", payload)
  # assert on response
end