FormKeep’s administrative interface is an Ember application that consumes a Rails-backed JSON API. We test our JSON endpoints with RSpec’s request specs.
Ensuring that the client and server adhere to a common contract is important. Without test coverage, the contract is implicit and often times brittle. Implementation changes on the server that change JSON payloads in unexpected ways pose problems for consumers.
To guard against unexpected changes, we created json_matchers to validate the
format of our JSON endpoints, making our contract explicit. Under
the covers, json_matchers
validates payloads against schemas conforming to the
JSON Schema specification. Schemas serve as a codification of the
client-server contract.
Asserting responses adhere to a JSON Schema
In order to verify that GET /api/forms
serves JSON in the format our client
expects, we’ll assert that it successfully validates against a particular
JSON schema:
describe "GET /api/forms" do
it "returns the user's forms" do
user = create(:user)
form = create(:form, user: user)
create(:form)
json_get "/api/forms", api_token: user.api_token
expect(response).to be_successful
expect(response).to match_response_schema("forms-many")
end
end
def json_response
JSON.parse(response.body)
end
Next, we’ll create the forms-many
schema, declared in
spec/support/api/schemas/forms-many.json
:
{
"type": "object",
"required": [
"forms"
],
"properties": {
"forms": {
"type": "array",
"items": { "$ref": "form.json" }
}
}
}
Note the "$ref": "form.json"
, key-value pair. It denotes a
canonical dereferencing, which allows us to reference and reuse a
form
schema that we declare separately in
spec/support/api/schemas/form.json
:
{
"type": "object",
"required": [
"id",
"name",
"redirect_url",
"webhook_url",
"field_map",
"sandboxed"
],
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"redirect_url": { "type": "string" },
"webhook_url": { "type": "string" },
"field_map": { "type": ["object", "null"] },
"sandboxed": { "type": "boolean" }
}
}
These two schemas, combined with the match_response_schema
matcher,
guarantee the format, structure, and value types of the API response. Schema
changes necessitate serializer code changes, and vice versa.
This is a good thing, as it helps guarantee that your client’s expectations match the server’s behavior.
Next steps
- Read Validating JSON Schemas with an RSpec Matcher.
- Add json_matchers to your project.
- Help make json_matchers great!