RSpec::JsonApi

RSpec:JsonAPI is an extension for RSpec to easily allow testing JSON API responses.

Installation

Add this line to your application's Gemfile:

gem 'rspec-json_api'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install rspec-json_api

Generate directory tree:

rails generate rspec:json_api:install

Require gem assets in your rails_helper.rb

Dir[File.join(__dir__, 'rspec', 'json_api', 'types', '*.rb')].each { |file| require file }
Dir[File.join(__dir__, 'rspec', 'json_api', 'interfaces', '*.rb')].each { |file| require file }

Generators

Using build-in generators it's possible to create custom interface and type.

Generate new template:

rails generate rspec:json_api:interface interface-name

Generate new type:

rails generate rspec:json_api:type type-name

Example usage

# spec/controllers/users_controller_spec.rb

RSpec.describe UsersController, type: :controller do
  describe '#index' do
    let(:expected_schema) do
      Array[{
        id: RSpec::JsonApi::Types::UUID,
        name: String,
        age: Integer,
        favouriteColorHex: /^\#([a-fA-F]|[0-9]){3,6}$/,
        number: -> { { type: Integer, min: 10, max: 20, lambda: ->(actual) { actual.even? } } }
      }]
    end

    it 'matches API response' do
      get :index

      expect(response.body).to match_json_schema(expected_schema)
    end
  end

  describe '#update' do
    it 'matches API response' do
      put :update, params: { name: 'John', age: 35 }

      expect(response.body).to have_no_content
    end
  end
end

Built-in matchers

  • match_json_schema

    expect(response.body).to match_json_schema(expected_schema)
    
  • have_no_content

    expect(response.body).to have_no_content
    

Interfaces

The gem introduces interfaces to reuse them during test matches.

# spec/rspec/json_api/interfaces/example_interface.rb

module RSpec
  module JsonApi
    module Interfaces
      EXAMPLE_INTERFACE = {
        id: Types::UUID,
        name: String,
        number: Integer,
        color: -> { { inclusion: %w[black red white], allow_blank: true } }
      }.freeze
    end
  end
end

Note: You can either generate file on your own or use generator.

Types

The gem allow users either to user build-in types or define owns.

Build-in types

  • #### EMAIL ruby RSpec::JsonApi::Types::EMAIL
  • #### URI ruby RSpec::JsonApi::Types::URI
  • #### UUID ruby RSpec::JsonApi::Types::UUID

Custom type example:

# spec/rspec/json_api/types/color_hex.rb

module RSpec
  module JsonApi
    module Types
      COLOR_HEX = /^#(?:[0-9a-fA-F]{3}){1,2}$/
    end
  end
end

RSpec::JsonApi::Types::COLOR_HEX

Note: You can either generate file on your own or use generator.

Matching methods

The gem offers variety of possible matching methods.

Presumptions

  • match_json_schema always require full keys match.

Failure Example: ```ruby let(:expected) do { id: RSpec::JsonApi::Types::UUID, name: String, age: Integer } end

let(:actual) do
  {
    id: "0a2f911f-3767-4cc7-9c19-049f4350e38c",
    name: "Mikel",
  }
end
```

Success Example:

  let(:expected) do
    {
      id: RSpec::JsonApi::Types::UUID,
      name: String,
      age: Integer
    }
  end

  let(:actual) do
    {
      id: "0a2f911f-3767-4cc7-9c19-049f4350e38c",
      name: "John",
      age: 24
    }
  end

Value match

let(:expected_schema) do
  {
    id: "e0067346-4d24-4aa6-b303-f927a410a001",
    name: "John",
    age: 24,
    favouriteColorHex: "#FF5733"
  }
end

Class match

let(:expected_schema) do
  {
    id: Integer,
    name: String,
    age: Integer,
    notes: Array[String]
  }
end

Type match

let(:expected_schema) do
  {
    id: RSpec::JsonApi::Types::UUID,
    email: RSpec::JsonApi::Types::EMAIL,
  }
end

Regexp match

let(:expected_schema) do
  {
    color: /^\#([a-fA-F]|[0-9]){3,6}$/
  }
end

Interface match

let(:expected_schema) do
    Array[RSpec::JsonApi::Interfaces::PERSON]
end

Proc match

Proc match allows to customize schema accoring needs using lambda shorthand notation ->

Supported options:

  • #### type ruby let(:expected_schema) do { name: -> { { type: String } } } end
  • #### value ruby let(:expected_schema) do { name: -> { { value: "John" } } } end
  • #### min ruby let(:expected_schema) do { age: -> { { min: 15 } } } end
  • #### max ruby let(:expected_schema) do { age: -> { { max: 25 } } } end
  • #### inclusion ruby let(:expected_schema) do { letter: -> { { inclusion: %w[A B C] } } } end
  • #### regex ruby let(:expected_schema) do { hex: -> { { regex: /^\#([a-fA-F]|[0-9]){3,6}$/ } } } end
  • #### lambda ruby let(:expected) do { number: -> { { lambda: ->(actual) { actual.even? } } } } end
  • #### allow_blank
let(:expected_schema) do
  {
    name: -> { { type: String, allow_blank: true } }
  }
end

Note: Default value is false

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nomtek/rspec-json_api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the RSpec::JsonApi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.