Tired of json-schema, try dry-validation
Tags: rspec, dryvalidation, jsonschema, ruby
Published: 2018-09-21 05:56:46

---
title: Tired of json-schema, try dry-validation
published: true
description: Validating JSON schemas with rspec + dry-validation
tags: rspec, dry-validation, json-schema, ruby
cover_image: https://philsturgeon.uk/images/article_images/2018-04-13-openapi-and-json-schema-divergence-solved/swagger-editor-unhappy.png
---

### Why you should use dry-validation over json-schema?
You working with JSON responses coming from APIs and you want to validate their schemas? What’s your first choice? Probably you go with the great way that Thoughtbot guys explained in this article. I’ve launched and tested various projects based on that technique but I always felt I’m doing something wrong because the process was so painful. Let me explain; If you want to validate a JSON response first you should create a JSON schema based on it then you’d write your test and get some errors because of a comma/double quotation being missed.

![errors everywhere](https://cdn-images-1.medium.com/max/1600/1*inL-FgfOPxYjynFh5-oNfA.png)

An example of the old way of testing JSON responses. First, define the JSON schema:

```json

{
  "type": "object",
  "required": ["user"],
  "properties": {
    "user" : {
      "type" : "object",
      "required" : [
        "id",
        "token",
        "token_expired_at",
        "email",
        "firstname",
        "lastname",
        "gender",
        "phone_number",
        "birthday"
      ],
      "properties" : {
        "id" : { "type" : "integer" },
        "token" : { "type" : "string" },
        "token_expired_at" : { "type" : "string" },
        "email" : { "type" : "string" },
        "lastname" : { "type" : "string" },
        "firstname" : { "type" : "string" },
        "gender" : { "type" : "string" },
        "phone_number" : { "type" : "string" },
        "brithday" : { "type" : "string", "format": "date-time" },
        "updated_at" : { "type" : "string", "format": "date-time" },
        "created_at" : { "type" : "string", "format": "date-time" },
      }
    }
  }
}
```

Then write your test

```ruby
describe "Sign in" do
  context "anonymous user" do
    it "returns data in the specified format" do
      sign_in_as_anonymous

      expect(response).to match_response_schema("users/anonymous_detail")
    end
  end
end
```

Not speaking of bad errors, you have to switch to JSON schema syntax and Ruby over and over again (context switching) and there you are you’ve lost your time and focus. Now I’m going to show you a better way to validate your mighty JSON with ease.

### Creating the custom matcher

```ruby
# Gemfile
group :test do
  gem "dry-validation"
end
```

```ruby
# spec/support/matchers/match_schema.rb
RSpec::Matchers.define :match_schema do |schema|
  match do |response|
    @result = schema.call(JSON.parse(response.body, symbolize_names: true))
    @result.success?
  end
  
  def failure_message
    @result.errors
  end
end
```

> Note: Here I’ve chosen `/spec/support/matchers/*` for my custom matchers with a name convention of `match_*`. Also in `schema.call` `schema` is a dry-validation class and `call` initializes the schema class with the given parameters then I return the result of validation which is a true/false value. I’ve even defined `failure_message` to be able to see the errors that have been generated from validating the schema inside Rspec result.


### Defining the anonymous user schema

Add this to your `spec/rails_helper.rb`

```ruby
require "support/views/schemas"
```

Now defining anonymous user schema:

```ruby
AnonymousUserSchema = Dry::Validation.Schema do
  required(:user).schema do
    required(:email)
    required(:token).filled
    required(:token_expired_at).filled
    required(:firstname)
    required(:lastname)
    required(:gender)
    required(:phone_number)
    required(:birthday)
  end
end
```

Now you can change your spec to this:

```ruby
describe "Sign in" do
  context "anonymous user" do
    it "returns data in the specified format" do
      sign_in_as_anonymous

      expect(response).to match_schema(AnonymousUserSchema)
    end
  end
end
```

There you go! What if when I added a new field? You just add the field with the needed condition to end of the block.

```ruby
AnonymousUserSchema = Dry::Validation.Schema do
  required(:user).schema do
    required(:email)
    required(:token).filled
    required(:token_expired_at).filled
    required(:firstname)
    required(:lastname)
    required(:gender)
    required(:phone_number)
    required(:birthday)
    required(:age).maybe(int?)
  end
end
```

With `dry-validation` you can do pretty much everything. For further information please check out the link down below.

http://dry-rb.org/gems/dry-validation/


I hope you have enjoyed :D