The json-schema landscape in ruby is a bit unusual.
The json_matchers
gem has integration with RSpec/Minitest and good support for json-schema features (including resolving $ref
s), however.
It uses the json-schema
gem underneath, and inherits its featureset.
This library doesn’t appear to be well maintained, and only supports json-schema draft-05.
This is a good compromise (for my use case) but another option may be more suitable if draft-05 is too old.
The following steps assume you’re building a rails app, and testing it with rspec and vcr.
-
Install
json_matchers
in the:test
gem groupgroup :test do gem "json_matchers" end
-
Configure the integration (in your
spec_helper.rb
orspec/support/
directory)require "json_matchers/rspec" JsonMatchers.schema_root = "spec/schemas"
-
Create a schema, e.g.
spec/schemas/v2/error.json
:{ "$schema": "https://json-schema.org/draft-06/schema#", "title": "API v2: Error Response", "type": "object", "additionalProperties": false, "additionalItems": false, "required": ["status", "message"], "properties": { "status": { "type": "string" }, "message": { "type": "string" } } }
-
Check the response matches the schema
# spec/requests/v2/postcodes_controller_spec.rb describe V2::PostcodesController, :vcr do context "with an invalid postcode" do before { get "/v2/connectivity/for_postcode/not_a_postcode" } it { is_expected.to have_http_status :bad_request } it { is_expected.to match_json_schema("v2/error") } end end
The alternative–and a strategy you probably still need if you want to make expectations about the exact response coming back from the system under test–is to take advantage of rspec composable matchers. For example:
# spec/requests/v1/products_controller_spec.rb
describe V1::ProductsController do
it "returns a list of product names, codes and categories" do
get "/v1/products"
products = response.parsed_body.fetch("products", [])
expect(products).to all(a_hash_including(
"name" => a_string_matching(/^[\dG]+\/[\dG]+$/),
"code" => a_string_matching(/FIBRE_\d+_\d+/),
"category" => kind_of(String),
))
end
end
The advantages of using json-schema here are several:
- less repetitive tests: you can just say “the response matches the schema”
- json-schema can be used in combination with OpenAPI (some light preprocessing may be required–this may be something to document later); the advantage being you can use the same schema in your documentation and your tests
- once you can read json-schema, it provides a common format for describing the underlying data, and, in my opinion is much easier to read than a lengthy composable matchers block