Match API output with YAML fixture

A nice and simple approach to test you JSON APIs:

# spec/features/api/user_spec.rb

require 'rails_helper'

RSpec.feature 'User', type: :feature do
  it 'lists users' do
    user = create(:user, :as_admin)
    visit api_user_path(user, format: :json)
    expect(page).to match_yaml_fixture('api/user')
  end
end

This checks if the page source matches the data given in the fixture file. To make it more readable we format the expected JSON as YAML:

# spec/fixtures/api/user.yaml

---
user:
- id: 1
  first_name: Max
  last_name: Power
  role:
    id: 1
    name: Admin

Add match_yaml_fixture to RSpec

Drop this in your support directory:

require 'rspec/expectations'

module FixtureHelper
  def read_fixture(*path_elements)
    IO.read(Rails.root.join("spec", "fixtures", *path_elements))
  end
end

RSpec::Matchers.define :match_yaml_fixture do |expected|
  match do |actual|
    expected = prepare_expected(expected)
    actual = prepare_actual(actual)
    actual.strip == read_fixture(expected).strip
  end
  failure_message do |actual|
    expected = prepare_expected(expected)
    actual = prepare_actual(actual)
    "expected the following to match fixture file #{expected}:\n\n#{actual}"
  end

  private

  def prepare_expected(expected)
    expected += '.yaml' unless expected.end_with?('.yaml')
    expected
  end

  def prepare_actual(actual)
    actual = JSON.parse(actual.source) if actual.is_a?(Capybara::Session)
    actual = actual.to_yaml if actual.is_a?(Hash)
    actual.to_s
  end
end

Then include the helper in your spec_helper.rb or rails_helper.rb file:

# spec/rails_helper.rb

require File.expand_path('../support/fixture_helper', __FILE__)
RSpec.configure do |config|
  config.include FixtureHelper
end