New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds :have_valid_schema RSpec matcher #1947

Closed
wants to merge 14 commits into
base: master
from

Conversation

Projects
None yet
7 participants
@leonelgalan
Contributor

leonelgalan commented Oct 17, 2016

Purpose

Provides built-in matchers for RSpec users. Example:

require 'rails_helper'
require 'active_model_serializers/rspec'
ActiveModelSerializers.config.schema_path = 'spec/support/schemas'

RSpec.describe V1::PostsController do
  describe '#index' do
    before { get :index }

    describe 'response' do
      subject { response }
      it { is_expected.to have_valid_schema }
    end
  end
end

Changes

Adds modules:

  • ActiveModelSerializers::RSpecMatchers::Schema

Caveats

  • No tests, I'm still trying to figure out how to run rspec next to minitest and translate test/active_model_serializers/test/schema_test.rb to RSpec.
  • Couldn't use autoload, expected path would be /active_model_serializers/r_spec/matchers, as opposed to: /active_model_serializers/rspec/matchers. Doesn't apply anymore, see below.
  • Had to require 'minitest/autorun' on lib/active_model_serializers/test/schema.rb. Not anymore.

I could use some help solving this issues.

Related GitHub issues

@mention-bot

This comment has been minimized.

mention-bot commented Oct 17, 2016

@leonelgalan, thanks for your PR! By analyzing the history of the files in this pull request, we identified @maurogeorge, @bf4 and @beauby to be potential reviewers.

@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Oct 17, 2016

Couldn't use autoload, expected path would be /active_model_serializers/r_spec/matchers, as opposed to: /active_model_serializers/rspec/matchers

is there a reason this is a bad thing?

I imagine to use this, we'd want to:

require 'active_model_serializers/rspec/matchers'

it's a common pattern in other gems to do it this way, I think.

unless you mean that you wanted to autoload the seperate files for rspec matchers.
autoload takes a second parameter that points to a file

end
class Base < ActiveModelSerializers::Test::Serializer::AssertSerializer
def subscribe

This comment has been minimized.

@NullVoxPopuli

NullVoxPopuli Oct 17, 2016

Contributor

is this required for an rspec matcher?

This comment has been minimized.

@leonelgalan

leonelgalan Oct 17, 2016

Contributor

What do you mean by this?

  • I called the class Base for the lack of a better name. It's already inside ActiveModelSerializers::RSpec::Matchers::Serializer so it doesn't need any more specification.
  • I'm changing the subscribe method for my own, so it works with CollectionSerializers.

This comment has been minimized.

@NullVoxPopuli

NullVoxPopuli Oct 18, 2016

Contributor

just, why does subscribe need to exist?

Are you saying that the built-in subscribe doesn't work for CollectionSerializer's?

This comment has been minimized.

@leonelgalan

leonelgalan Oct 18, 2016

Contributor

It doesn't , the behavior without that change is the one described by @rafaelgonzalez on #1470 (comment). To be honest, I haven't tested this outside of my own matchers (directly on Minitest using builtin assertions). I will do a report back and write a failing test.

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Oct 17, 2016

Not necessarily a bad thing, I just would have prefer not to change the existing files while adding the RSpec matchers.

I only wanted to use autoload, because that's how the the current files are loaded and I want to deviate the least from what's currently implemented. I will try that second param, but currently I simply require the file like you said.

@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Oct 18, 2016

I read you. the problem with require with how you have it is that when lib/active_model_serializers/rspec/matchers.rb is required, the matcher files are also required. So for those that don't use rspec, or even during production use, that would lead to these rspec helpers erroring, because not all of the methods are defined. for the initial require of matchers, maybe you'll want do something like:

autoload RSpecMatchers, 'rspec/rspec_matchers' if defined? RSpec

I believe that will also protect against accidental requiring of the matchers in a non-test environment, provided that RSpec is only in the test env. But, def something you want to test out.

Also note that I changed the name of RSpec/Matchers to RSpecMatchers -- this is to prevent any module/class changes to rspec in the future.

So,

# lib/active_model_serializers/rspec/matchers.rb
module ActiveModelSerializers
  module RSpecMatchers
    # @api public
    # ...

I think that'll work, but anywho, give it a go?!

Thanks for your work on this! I'll def use these matchers in my own project :-)

@@ -15,6 +15,8 @@ module ActiveModelSerializers
autoload :JsonPointer
autoload :Deprecate
autoload :RSpecMatchers, 'active_model_serializers/rspec/matchers' if defined? RSpec

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

✂️

instructions should be put require 'active_model_serializers/rspec' in your rails_helper.rb just like capybara/rspec, it should be opt-in.

This comment has been minimized.

@NullVoxPopuli

NullVoxPopuli Oct 20, 2016

Contributor

👍

This comment has been minimized.

@leonelgalan

leonelgalan Oct 20, 2016

Contributor

Agreed, I will make the changes necessary for this instructions to work. Also, I'll add the changes to docs/howto/test.md

end
require_relative 'matchers/serializer'
require_relative 'matchers/schema'

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

require 'active_model_serializers/rspec/matcher/serializer' etc.

extend ActiveSupport::Concern
included do
RSpec::Matchers.define :have_valid_schema do |schema_path|

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

No need for a concern here. Just define the matcher. what's Base? should use a fully qualified path.

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

Could probably be ( just me typing here, expect errors)

      RSpec::Matchers.define :have_valid_schema do |api_schema|
          match do |request_or_response|
            schema_type = request_or_response.respond_to?(:env) && :request
            schema_type ||= request_or_response.respond_to?(:success?) &&  :response
            case schema_type
            when :request
                expect(request_or_response).to be_a_valid_api_request
                assert_request_schema api_schema
            when :response
                expect(request_or_response).to be_a_valid_api_response
                assert_response_schema api_schema
             else 
                 fail UnknownSchemaType.new("#{request_or_response} cannot be identified as a request or response")
             end
          end       
        end

alternatively, if we don't want a global matcher

included do
    require 'rspec/expectations'
    extend RSpec::Matchers::DSL

    matcher :have_valid_schema do |api_schema|
      match {|request_or_response|
      }
    failure_message {|request_or_response|
    failure_message_when_negated {|request_or_response|
    # etc.

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

see #1947 (comment) for some more ideas

It may be helpful for the matcher to rely on methods that can be overridden

In my experience, request and response matchers are different and it's very useful to validate headers.

This comment has been minimized.

@leonelgalan

leonelgalan Oct 20, 2016

Contributor

Ok, I will explore the suggested code and the one in the comment

This comment has been minimized.

@leonelgalan

leonelgalan Oct 24, 2016

Contributor

Base was just poor naming, now that I move my "fix" to AssertSchema, I can simply initialize that class and use it as intended.

setup :setup_serialization_subscriptions
teardown :teardown_serialization_subscriptions
RSpec::Matchers.define :use_serializer do |expected|

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

Same comment except that this is really just a very strange thing to test in the first place. I personally don't get why people like it and understand why it's equivalent assert was removed from Rails. All you're asserting is that the serializer you want to use but aren't being explicit about is being looked up and used implicitly. If you care about which serializer is used enough to test it, just specify it in the render options in the controller.

My 2 💰

Also, no need for the setup and teardown

This comment has been minimized.

@leonelgalan

leonelgalan Oct 20, 2016

Contributor

I agree, for me the JSON Schema is more important, because no matter what serializer is being used I simply want the response to be what I expect. That said, I was simply translating the current asserts to it's RSpec equivalents.

These setup and teardown set the subscriber needed to know what Serializer is being used on the render.

This comment has been minimized.

@NullVoxPopuli

NullVoxPopuli Oct 20, 2016

Contributor

do we even need the subscriber in testing though?

This comment has been minimized.

@leonelgalan

leonelgalan Oct 20, 2016

Contributor

It's the subscriber to the::ActiveModelSerializers::Logging::RENDER_EVENT that allows us to see what serializer was used. At least that's how the current assertion works. That is, of course, if we want to keep this assertion/matcher. See https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/test/serializer.rb#L67

This comment has been minimized.

@bf4

bf4 Oct 21, 2016

Member

I'd rather just not include the use_serializer in this PR if even @leonelgalan doesn't use it.

This comment has been minimized.

@leonelgalan

leonelgalan Oct 24, 2016

Contributor

Sounds good, I'll remove this. I was on a weekend vacation, but I'm ready to get this PR moving again.

@@ -1,3 +1,5 @@
require 'minitest/autorun'

This comment has been minimized.

@bf4

bf4 Oct 20, 2016

Member

This is a super bad idea. ✂️

This comment has been minimized.

@leonelgalan

leonelgalan Oct 20, 2016

Contributor

I guess I should remove this and find another way to make it work? Never seen the ✂️ in a review before? I'll start by cutting this line and not pasting it anywhere else.

This comment has been minimized.

@bf4

bf4 Oct 21, 2016

Member

👅 Thanks for reading my comment in a positive light. Without knowing what problem you were working around, I wasn't sure what to write and didn't word it so well. autorun, specificaly, is almost certainly not what you want. no special require should be necessary here, except maybe ensure Minitest::Assertion is defined.

I usually use ✂️ and see it used to mean 'snip'.

@bf4

This comment has been minimized.

Member

bf4 commented Oct 20, 2016

Here's the code I have:

# Usage:
#   let(:headers) { api_headers(3) }
#    RSpec.describe Api::V3::BaseController, :jsonapi, type: :request do
#      it "responds with 401 if user does not exist" do # etc.
#      it 'responds with a 404 when the JSON API Accept header is missing',          :show_exceptions, jsonapi: [:not_response, :not_request] do # etc.
#    end
module ApiTesting
  include ActiveModelSerializers::Test::Schema

  def api_headers(version)
    if version == 3
      {
        'Accept' => "application/vnd.api+json",
        'Content-Type' =>  'application/vnd.api+json'
      }
    else
      {
        'Accept' => "application/vnd.example.v#{version}",
        'Content-Type' =>  'application/json'
      }
    end.merge!(
      'HTTP_HOST' => 'api.example.com',
      'connection' => 'close'
    )
  end

  def request_payload
    request.env["action_dispatch.request.request_parameters"].dup
  end

  def json_response
    JSON.parse(response.body, symbolize_names: true)
  end

  def response_content_type
    response.headers['Content-Type']
  end

  def request_accepts
    request.headers['Accept']
  end

  def jsonapi_request?
    # request.format == :jsonapi # Doesn't work for some reason
    request.accepts.any?(&:jsonapi?) &&
      request_accepts.include?(jsonapi_content_type)
  end

  def jsonapi_response?
    return true if response.body.empty? && response_content_type.nil? && jsonapi_request?
    response_content_type == "#{jsonapi_content_type}; charset=utf-8"
  end

  # FIXME: bug in api schema that erroneously requires 'id' on POST create
  def assert_jsonapi_request!
    return unless jsonapi_request? && request_payload.present?
    if request.post?
      payload = request_payload
      payload['data']['id'] = 'create_does_not_require_id'
      assert_schema payload, api_schema
    else
      assert_request_schema api_schema
    end
  end

  def assert_jsonapi_response!
    return unless jsonapi_response? && response.body.present?
    assert_response_schema api_schema
  end

  def assert_not_jsonapi_response!
    return unless response.body.present?
    error = assert_raises Minitest::Assertion do
      assert_response_schema 'jsonapi.json'
    end
    assert_match(/failed schema/, error.message)
  end

  # from https://github.com/json-api/json-api/blob/c4df8dae8c8b1e97169b6d92193b62749240fbc5/schema
  def api_schema
    'jsonapi.json'
  end

  def jsonapi_content_type
    'application/vnd.api+json'
  end
end
RSpec.configure do |config|
  config.include ApiTesting, type: :request

  jsonapi_spec_missing_api_testing = ->(v) { !!v && !self.class.included_modules.include?(ApiTesting) } # rubocop:disable Style/DoubleNegation
  config.include ApiTesting, jsonapi: jsonapi_spec_missing_api_testing
  # When metadata has key `:jsonapi`,
  # perform validations according to its values.  Defaults to all validations.
  # @usage any of:
  #   `:jsonapi`,
  #   `jsonapi: true`
  run_all = ->(v) { v.nil? || v == true }
  hashify = ->(input) { input.is_a?(Hash) ? input : (input == true ? {} : input.to_h) } # rubocop:disable Style/NestedTernaryOperator
  includes = ->(input, value) { hashify.(input).key?(value) }
  excludes = ->(input, value) { Array(hashify.(input)[:except]).include?(value) }
  only = ->(input, value) { Array(hashify.(input)[:only]).include?(value) }

  # @usage any of:
  #   `:jsonapi`,
  #   `jsonapi: true`
  #   `jsonapi: :request`
  #   `jsonapi: [:request]`
  validate_request_schema = ->(v){ run_all.(v) || includes.(v, :request) }
  config.after jsonapi: validate_request_schema do
    next if request.nil?
    assert_jsonapi_request!
  end

  # @usage
  #   disable request validation with any of:
  #   `jsonapi: { except: :request }`
  #   `jsonapi: { except: [:request] }`
  validate_request_accepts_media_type = ->(v){ !excludes.(v, :request) }
  config.after jsonapi: validate_request_accepts_media_type do
    next if request.nil?
    expect(jsonapi_request?).to eq(true)
  end

  # @usage any of:
  #   `:jsonapi`
  #   `jsonapi: true`
  #   `jsonapi: :response`
  #   `jsonapi: [:response]`
  validate_response_schema = ->(v){ run_all.(v) || includes.(v, :response) }
  config.after jsonapi: validate_response_schema do
    next if response.nil?
    assert_jsonapi_response!
  end

  # @usage disable with response validation with any of:
  #   `jsonapi: { except: :response }`
  #   `jsonapi: { except: [:response] }`
  validate_response_content_type = ->(v){ !excludes.(v, :response) }
  config.after jsonapi: validate_response_content_type do
    next if response.nil?
    expect(jsonapi_response?).to eq(true)
  end

  # @usage expect non-JSON API repsonse
  #   `jsonapi: { except: :response }`
  #   `jsonapi: { except: [:response] }`
  assert_response_schema_not_jsonapi = ->(v){ excludes.(v, :response) }
  config.after jsonapi: assert_response_schema_not_jsonapi do
    next if response.nil?
    assert_not_jsonapi_response!
  end
end

leonelgalan added some commits Oct 24, 2016

Fixes controller_path and action retreival in AssertSchema
rails (5.0.0.1) and active_model_serializers (0.10.2), not providing a
path for the schema results in: “No Schema file at
test/support/schemas//.json”, this patch allows the assertion to
retrieve the file successfully from test/support/schemas/posts/show.json

@leonelgalan leonelgalan changed the title from Adds :use_serializer and :have_valid_schema RSpec matchers to Adds :have_valid_schema RSpec matcher Oct 24, 2016

@NullVoxPopuli

this is much simpler than before. Is there anyway you can add an rspec test to verify that it works?

@@ -105,6 +105,32 @@ default schema path in an initializer.
ActiveModelSerializers.config.schema_path = 'spec/support/schemas'
```
### RSpec test helpers

This comment has been minimized.

@NullVoxPopuli

NullVoxPopuli Oct 24, 2016

Contributor

wooo docs!

@leonelgalan

Example of failing tests for 93de2c7 https://github.com/leonelgalan/active_model_serializer_example/blob/master/test/controllers/post_controller_test.rb

Both tests produce: No Schema file at test/support/schemas//.json

Somehow, it works on the current tests (I couldn't write a failing test for this).

I'm working on adding the specs for this PR.

leonelgalan added some commits Oct 24, 2016

Translates schema_test.rb to schema_spec.rb
Do we need to test behavior on both, or test the AssertSchema class
once?
Removes --color, prevents rubocop from running after spec.
It appears the option is passed to minutest runner:

```
…
invalid option: --color

minitest options:
    -h, --help                       Display this help.
…
```
@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Oct 25, 2016

So close @leonelgalan!

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Oct 25, 2016

Getting there, I'll fix Rails 4.1 Issues, should I rebase the PR to squash all this "fix" commits into the feature?

@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Oct 25, 2016

don't worry about rebasing or squashing, we'll handle that (Github has a squash and merge button)

@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Oct 25, 2016

WOAH! tests passing

@yijia-zhan

This comment has been minimized.

yijia-zhan commented Oct 31, 2016

This is awesome! 👍

@NullVoxPopuli

This comment has been minimized.

Contributor

NullVoxPopuli commented Nov 24, 2016

needs changelog entry.

@leonelgalan can you add that?

Gemfile Outdated
@@ -42,6 +42,7 @@ group :bench do
end
group :test do
gem 'rspec-rails'

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Anything required to run tests should be in gemspec. I am also inclined to think this should be its own gem. I 'll help

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

If you wanna steal, here's what I do

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

I'll move it to the gemspec. Didn't understand your second comment, maybe is missing a link?

require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = Dir.glob('spec/**/*_spec.rb')
end

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Consider 'mrspec' :)

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

I'll leave that up to you (rails-api members) to decide. For me adding mrspec it's just another layer on top of what already works.

@@ -60,11 +60,11 @@ def call
attr_reader :document_store
def controller_path
request.filtered_parameters[:controller]
request.filtered_parameters.with_indifferent_access[:controller]

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Was this a problem? I use this extensively without

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

See #1947 (review)

I couldn't write a failing test (in the existing suite), but there is a sample app with a test failing.

require 'action_controller/test_case'
require 'active_model_serializers'
$LOAD_PATH.unshift File.expand_path('../../test', __FILE__)

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Oy, we really need a dummy app

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

oh, this is so we can require the test helpers in the test dir...

end
end
describe ActiveModelSerializers::Test::SchemaTest::MyController, type: :controller do

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Disable monkey patch mode please

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

What do you mean?

describe ActiveModelSerializers::Test::SchemaTest::MyController, type: :controller do
routes { Routes }
include ActiveModelSerializers::RSpecMatchers::Schema

This comment has been minimized.

@bf4

bf4 Nov 26, 2016

Member

Shouldn't need to include it manually

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

So you want it that by including require 'active_model_serializers/rspec ActiveModelSerializers::RSpecMatchers::Schema is included on all controller and request specs?

This comment has been minimized.

@leonelgalan

leonelgalan Nov 28, 2016

Contributor

Something like this?
https://gist.github.com/leonelgalan/b63d031ece0f3989185c087be3cea285

api/lib/active_model_serializers/rspec.rb:

module ActiveModelSerializers
  # @api public
  # Container module for active_model_serializers specific matchers.
  module RSpecMatchers
    extend ActiveSupport::Autoload
    autoload :Schema, 'active_model_serializers/rspec_matchers/schema'
  end
end

RSpec.configure do |config|
  config.include ActiveModelSerializers::RSpecMatchers::Schema, type: :request
  config.include ActiveModelSerializers::RSpecMatchers::Schema, type: :controller
end

This comment has been minimized.

@bf4

bf4 Dec 7, 2016

Member

Yes, but no need for the autoload, IMO

RSpec::Matchers.define :have_valid_schema do |schema_path, message|
match do
@matcher = ActiveModelSerializers::Test::Schema::AssertSchema.new(
schema_path || nil, request, response, message || nil

This comment has been minimized.

@bf4

bf4 Dec 7, 2016

Member

schema_path || nil, request, response, message || nil

weird looking...

(schema_path || nil), request, response, (message || nil)

?

I'm thinking schema_path should be chained

  chain :at_path do |schema_path|
    @schema_path = schema_path
  end

leonelgalan added some commits Dec 20, 2016

Removes need to include Schema matcher manually
Simply doing `require 'active_model_serializers/rspec’` is enough to
load the schema matcher. Removes autoload as suggested by @bf4, uses
require instead.
module ActiveModelSerializers
module RSpecMatchers
module Schema
RSpec::Matchers.define :have_valid_schema do |message|

This comment has been minimized.

@bf4

bf4 Dec 21, 2016

Member

I'm not sure about the message expectation

This comment has been minimized.

@leonelgalan

leonelgalan Dec 21, 2016

Contributor

Let's use RSpec default message handling instead. the only difference is that the custom message is not prepended to the original message, it substitutes it.

For example in ActiveModelSerializers::Test::SchemaTest::MyController test_that_raises_error_with_a_custom_message_with_a_invalid_schema

Instead of

oh boy the show is broken: #/name: failed schema #/properties/name: For 'properties/name', \"Name 1\... schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean.

we simply get the custom message:

oh boy the show is broken

Removes custom message handling from expectation
Uses RSpec’s default instead: pass a message as a second param to `.to`
 to override the `failure_message`.
@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Jan 12, 2017

Hey guys, what's left to get this merged? The only thing that I see is #1947 (comment)

@bf4

Leonelgalan, thanks for keeping on top of this. Sorry I'm being so tough on you, but this is something that can impose a burden on maintainers once included....

require 'active_model_serializers/rspec'
# spec/controller/posts_controller_spec.rb
describe PostsController do

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

should be RSpec.describe PostsController, type: :controller

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

But actually, we should give an example from a request spec, since controller specs aren't very useful and are going away. Some discussion in #1970 (comment)

RSpec.describe PostsController, type: :request do


end
# spec/controller/posts_controller_spec.rb
describe PostsController do
describe 'index' do

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

should be context 'GET /posts' do IMO, since that's more descriptive and works well with autodoc tools

and then, per https://github.com/rails-api/active_model_serializers/pull/1947/files#r96015833 get '/posts', {}, headers

describe 'index' do
it "should render right response" do
get :index
expect(response).to have_valid_schema

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

which schema is this using? I'd rather the example be

expect(request).to have_valid_schema(api_request_schema)
expect(response).to have_valid_schema(api_response_schema)

expect(request).to have_valid_schema(post_request_schema)
expect(response).to have_valid_schema(post_response_schema)
end
```
It's usage is on par with `assert_response_schema`. See [ActiveModelSerializers::Test::Schema](../../lib/active_model_serializers/test/schema.rb) and [ActiveModelSerializers::RSpecMatchers::Schema](../../lib/active_model_serializers/rspec_matchers/schema.rb)

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

typo: Its

Probably better, something like:

`expect(request).to have_valid_schema(path_to_schema)` can be understood as being translated to `assert_request_schema(path_to_schema)`. 
See [ActiveModelSerializers::Test::Schema](../../lib/active_model_serializers/test/schema.rb) and [ActiveModelSerializers::RSpecMatchers::Schema](../../lib/active_model_serializers/rspec_matchers/schema.rb) for documentation of the schema assertions.
require 'action_controller/test_case'
require 'active_model_serializers'
$LOAD_PATH.unshift File.expand_path('../../test', __FILE__)

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

oh, this is so we can require the test helpers in the test dir...

end
end
RSpec.describe ActiveModelSerializers::Test::SchemaTest::MyController, type: :controller do

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

needs request spec. see https://github.com/rails-api/active_model_serializers/pull/1947/files#r96015833

Also, the test app should be defined inside the RSpec block or namespace

this globally creates a ActiveModelSerializers::Test::SchemaTest::MyController

Might be a good case to start a dummy app like in #1414

@@ -18,6 +18,7 @@ Features:
- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee)
- [#1917](https://github.com/rails-api/active_model_serializers/pull/1917) Add `jsonapi_pagination_links_enabled` configuration option (@richmolj)
- [#1797](https://github.com/rails-api/active_model_serializers/pull/1797) Only include 'relationships' when sideloading (@richmolj)
- [#1947](https://github.com/rails-api/active_model_serializers/pull/1947) Adds :have_valid_schema RSpec matche (@leonelgalan)

This comment has been minimized.

@bf4

bf4 Jan 13, 2017

Member

Needs to be moved up now that there have been releases.

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Jan 13, 2017

@bf4 no worries I can only imagine how much work a gem of this size is. I'll work on the requested changes and post them soon.

@kurko

This comment has been minimized.

Member

kurko commented Jan 13, 2017

I know I'm not as involved in this project anymore, but I'm 👎 on adding RSpec inside this gem. We'll then have to add TestUnit, maybe mocha, and so on. These things should live outside this gem, as independent gems.

A couple years ago there were talks about merging AMS into Rails5. Although that didn't happen for N reason, making it a one-size-fits all gem moves it even farther away from that, besides forcing maintainers to maintain yet another dependency. When RSpec (and other dependencies) goes v4, v5, AMS will evolve slower.

@bf4

This comment has been minimized.

Member

bf4 commented Jan 31, 2017

@leonelgalan would you be interested in continuing this work in a new repository?

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Jan 31, 2017

I'll give it a try. It's been a busy past weeks. But I've schedule some time later on the afternoon to tackle this:

  • Create rspec-active_model_serializers repository.
  • Move all bits in this PR to new repo.
@bf4

This comment has been minimized.

Member

bf4 commented Jan 31, 2017

@leonelgalan I started one some time ago but never came back to it https://github.com/bf4/active_model_serializers-rspec I can move it over to rails-api if you wish or otherwise give you commit.

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Jan 31, 2017

I pushed my first take minutes before your comment: https://github.com/leonelgalan/rspec-active_model_serializers. The repo name with the rspec- prefix, comes from every other gem I found.

I would like to keep doing some cleanup/troubleshooting on the night before we make a decision on how to move forward. I'm ok hosting it myself or moving it into the rails-api organization, once you guys 👍 the code.

I have one broken test and code that fixes it, but that code looks like a NOOP to me and shouldn't affect the outcome, please see leonelgalan/rspec-active_model_serializers#1 for details.

 class ProfileSerializer < ActiveModel::Serializer
    attributes :name, :description
 +
 +  def name
 +    object.name
 +  end
  end

TODO (For today)

  • Take ownership of the tests and rewrite them for RSpec. The currrent tests is a translation of the existing tests. Instead of it 'test_that_assert_with_a_valid_schema' maybe the test should read it 'validates response with a valid schema'
  • Improve documentation on README: Document at_path
  • Remove warnings on tests: DEPRECATION WARNING: Using a dynamic :controller segment in a route is deprecated and will be removed in Rails 5.1.
@bf4

This comment has been minimized.

Member

bf4 commented Feb 1, 2017

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Feb 1, 2017

It's not much work and hopefully it will encourage other RSpec users to collaborate.

Except for that test that's not passing outlined in leonelgalan/rspec-active_model_serializers#1 I made all the changes I wanted yesterday. I'll keep troubleshooting that, but any help is welcomed.

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Feb 6, 2017

Everything is passing now, I'm going going to do some reading on the versioning of dependencies and publish the gem once I'm more comfortable with the gems and versions i'm putting in the .gemspec.

@jonwolski

This comment has been minimized.

jonwolski commented Feb 7, 2017

I see value in this feature, but I question whether or not it belongs in AMS itself. For example, I already use https://github.com/sharethrough/json-schema-rspec for matching against JSON schemata. (I would prefer not to add another RSpec helper a slightly different API.)
Is there something particular about matching against a JSON-API request or response, that gives AMS unique schema-matching needs?

@bf4

This comment has been minimized.

Member

bf4 commented Feb 7, 2017

@jonwolski JSON-SCHEMA validation is an opt-in test helper. My intention when driving to introduce it was to add it our tests, but that hasn't happened yet. This specific PR to add an RSpec helper should have been closed, as it will be introduced in a separate gem. I, myself, use RSpec-Rails and what AMS already gives me, as well.

(though I should note we use the json_schema gem, not json-schema, based on supported features, IIRC)

@bf4 bf4 closed this Feb 7, 2017

@leonelgalan

This comment has been minimized.

Contributor

leonelgalan commented Feb 7, 2017

@jonwolski, I know the PR is absurdly long now, but that's the conclusion we reach and move it to its own gem (about to be released). I didn't know about json-schema-rspec and from what I read, it looks very powerful and if you already use it you shouldn't add any other helper.

AMS provided test assertions do make it easier when you are testing the schemas within a request with a controller and action. This new gem makes it simpler for RSpec users to use those same assertions as a matcher.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment