Skip to content

Commit

Permalink
Create assert_response_schema test helper
Browse files Browse the repository at this point in the history
It is a common pattern to use JSON Schema to validate a API response[1], [2]
and [3].

This patch creates the `assert_response_schema` test helper that helps people do
this kind of validation easily on the controller tests.

[1]: https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher
[2]: https://github.com/sharethrough/json-schema-rspec
[3]: #1011 (comment)
  • Loading branch information
maurogeorge committed Oct 15, 2015
1 parent da7e6dc commit f2d50d1
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Features:
- [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested
associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby).
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
- [#1270](https://github.com/rails-api/active_model_serializers/pull/1270) Adds `assert_response_schema` test helper (@maurogeorge)

Fixes:
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)
Expand Down
2 changes: 2 additions & 0 deletions active_model_serializers.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Gem::Specification.new do |spec|
# 'minitest'
# 'thread_safe'

spec.add_runtime_dependency 'json-schema'

# Soft dependency for pagination
spec.add_development_dependency 'kaminari', ' ~> 0.16.3'
spec.add_development_dependency 'will_paginate', '~> 3.0', '>= 3.0.7'
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This is the documentation of AMS, it's focused on the **0.10.x version.**
- [How to add root key](howto/add_root_key.md)
- [How to add pagination links](howto/add_pagination_links.md)
- [Using AMS Outside Of Controllers](howto/outside_controller_use.md)
- [Testing AMS](howto/test.md)

## Getting Help

Expand Down
78 changes: 78 additions & 0 deletions docs/howto/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# How to test

## Test helpers

AMS provides a `assert_response_schema` method to be used on your controller tests to
assert the response against a [JSON Schema](http://json-schema.org/). Let's take
a look in a example.

```ruby
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])

render json: @post
end
end
```

To test the `posts#show` response of this controller we need to create a file in
`test/support/schemas/posts/show.json` the helper uses a convention to the name
of the file.

This file is a JSON Schema representation of our response.

```json
{
"properties": {
"title" : { "type" : "string" },
"content" : { "type" : "string" }
}
}
```

With all in place we can go to our test and use the helper.

```ruby
class PostsControllerTest < ActionController::TestCase
test "should render right response" do
get :index
assert_response_schema
end
end
```

### Load a custom schema

If we need to use other schema, for example when we have a namespaced API that
shows the same response, we can pass the path of the schema.

```ruby
module V1
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])

render json: @post
end
end
end
```

```ruby
class V1::PostsControllerTest < ActionController::TestCase
test "should render right response" do
get :index
assert_response_schema('posts/show.json')
end
end
```
### Change the schema path

By default all schemas are created at `test/support/schemas` if we are using
RSpec for example we can change this to `spec/support/schemas` defining the
default schema path in a initializer.

```ruby
ActiveModel::Serializer.config.schema_path = `spec/support/schemas`
```
16 changes: 16 additions & 0 deletions lib/active_model/serializer/assertions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'json-schema'

module ActiveModel
class Serializer
module Assertions
def assert_response_schema(schema_path = nil)
controller_path = response.request.filtered_parameters[:controller]
action = response.request.filtered_parameters[:action]
schema_directory = ActiveModel::Serializer.config.schema_path
schema_path ||= "#{controller_path}/#{action}.json"
schema_full_path = "#{schema_directory}/#{schema_path}"
JSON::Validator.validate!(schema_full_path, response.body, strict: true)
end
end
end
end
1 change: 1 addition & 0 deletions lib/active_model/serializer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Configuration
base.config.array_serializer = ActiveModel::Serializer::ArraySerializer
base.config.adapter = :attributes
base.config.jsonapi_resource_type = :plural
base.config.schema_path = 'test/support/schemas'
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/active_model_serializers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ def silence_warnings
require 'active_model/serializer'
require 'active_model/serializable_resource'
require 'active_model/serializer/version'
require 'active_model/serializer/assertions'

require 'action_controller/serialization'
ActiveSupport.on_load(:action_controller) do
include ::ActionController::Serialization
ActionDispatch::Reloader.to_prepare do
ActiveModel::Serializer.serializers_cache.clear
end
ActionController::TestCase.send(:include, ActiveModel::Serializer::Assertions)
end

require 'active_model/serializer/railtie'
46 changes: 46 additions & 0 deletions test/assertions_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'test_helper'

module ActiveModel
class Serializer
class AssertionsTest < ActionController::TestCase
class MyController < ActionController::Base
def index
render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
end

def show
index
end
end

tests MyController

def test_that_assert_with_a_valid_schema
get :index
assert_response_schema
end

def test_that_raises_a_json_schema_with_a_invalid_schema
get :show
assert_raises JSON::Schema::ValidationError do
assert_response_schema
end
end

def test_that_assert_with_a_custom_schema
get :show
assert_response_schema('custom/show.json')
end

def test_that_assert_with_a_custom_schema_directory
original_schema_path = ActiveModel::Serializer.config.schema_path
ActiveModel::Serializer.config.schema_path = 'test/support/custom_schemas'

get :index
assert_response_schema

ActiveModel::Serializer.config.schema_path = original_schema_path
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"properties": {
"name" : { "type" : "string" },
"description" : { "type" : "string" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"properties": {
"name" : { "type" : "string" },
"description" : { "type" : "string" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"properties": {
"name" : { "type" : "string" }
}
}
6 changes: 6 additions & 0 deletions test/support/schemas/custom/show.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"properties": {
"name" : { "type" : "string" },
"description" : { "type" : "string" }
}
}

0 comments on commit f2d50d1

Please sign in to comment.