Skip to content

Bring back assert_serializer for controller testing #1390

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

Merged
merged 9 commits into from
Jan 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Features:
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
- [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that,
when disabled, requires serializers to explicitly specified. (@trek)
- [#1099](https://github.com/rails-api/active_model_serializers/pull/1099) Adds `assert_serializer` 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
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10.
- [How to add root key](howto/add_root_key.md)
- [How to add pagination links](howto/add_pagination_links.md)
- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md)
- [Testing ActiveModelSerializers](howto/test.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍


## Integrations

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

## Test helpers

ActiveModelSerializers provides a `assert_serializer` method to be used on your controller tests to
assert that a specific serializer was used.

```ruby
class PostsControllerTest < ActionController::TestCase
test "should render post serializer" do
get :index
assert_serializer "PostSerializer"
end
end
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside the scope of this PR, but something link #1011 (comment) should probably be included here as well for unit testing serializers themselves

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trek Yeah, I intend to add that if someone else doesn't... maybe it would be good to make an issue? I've been meaning to write it up as code in #967 but it hasn't 'happened' yet.

In the meantime, I guess people can post links here :)

See [ActiveModelSerializers::Test::Serializer](../../lib/active_model_serializers/test/serializer.rb)
for more examples and documentation.
4 changes: 4 additions & 0 deletions lib/active_model/serializer/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@ class Railtie < Rails::Railtie
app.load_generators
require 'generators/serializer/resource_override'
end

if Rails.env.test?
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
end
end
end
1 change: 1 addition & 0 deletions lib/active_model_serializers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def self.config
autoload :Callbacks
autoload :Deserialization
autoload :Logging
autoload :Test
end

require 'active_model/serializer'
Expand Down
3 changes: 2 additions & 1 deletion lib/active_model_serializers/logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#
module ActiveModelSerializers
module Logging
RENDER_EVENT = 'render.active_model_serializers'.freeze
extend ActiveSupport::Concern

included do
Expand Down Expand Up @@ -73,7 +74,7 @@ def notify(name, callback_name)
end

def notify_render(*)
event_name = 'render.active_model_serializers'.freeze
event_name = RENDER_EVENT
ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do
yield
end
Expand Down
6 changes: 6 additions & 0 deletions lib/active_model_serializers/test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module ActiveModelSerializers
module Test
extend ActiveSupport::Autoload
autoload :Serializer
end
end
125 changes: 125 additions & 0 deletions lib/active_model_serializers/test/serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
require 'set'
module ActiveModelSerializers
module Test
module Serializer
extend ActiveSupport::Concern

included do
setup :setup_serialization_subscriptions
teardown :teardown_serialization_subscriptions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really need this teardown ? If we need, I think we need to write a broken test to it, what behavior will be broken if we do not have this teardown.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it needs a test.

It just unsubscribes the subscriber created in the test...

See more in the commit d9eefdb

I can try to show what may break without it, but given that the teardown is copied from rails, and I understand the behavior with and without...

B mobile phone

On Jan 5, 2016, at 1:05 PM, Mauro George notifications@github.com wrote:

In lib/active_model_serializers/test/serializer.rb:

@@ -0,0 +1,125 @@
+require 'set'
+module ActiveModelSerializers

  • module Test
  • module Serializer
  •  extend ActiveSupport::Concern
    
  •  included do
    
  •    setup :setup_serialization_subscriptions
    
  •    teardown :teardown_serialization_subscriptions
    
    We really need this teardown ? If we need, I think we need to write a broken test to it, what behavior will be broken if we do not have this teardown.


Reply to this email directly or view it on GitHub.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ref original comment 11fda59#commitcomment-15145827

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I am good with that.

end

# Asserts that the request was rendered with the appropriate serializers.
#
# # assert that the "PostSerializer" serializer was rendered
# assert_serializer "PostSerializer"
#
# # return a custom error message
# assert_serializer "PostSerializer", "PostSerializer not rendered"
#
# # assert that the instance of PostSerializer was rendered
# assert_serializer PostSerializer
#
# # assert that the "PostSerializer" serializer was rendered
# assert_serializer :post_serializer
#
# # assert that the rendered serializer starts with "Post"
# assert_serializer %r{\APost.+\Z}
#
# # assert that no serializer was rendered
# assert_serializer nil
#
def assert_serializer(expectation, message = nil)
@assert_serializer.expectation = expectation
@assert_serializer.message = message
@assert_serializer.response = response
assert(@assert_serializer.matches?, @assert_serializer.message)
end

class AssertSerializer
attr_reader :serializers, :message
attr_accessor :response, :expectation

def initialize
@serializers = Set.new
@_subscribers = []
end

def message=(message)
@message = message || "expecting <#{expectation.inspect}> but rendering with <#{serializers.to_a}>"
end

def matches?
# Force body to be read in case the template is being streamed.
response.body

case expectation
when a_serializer? then matches_class?
when Symbol then matches_symbol?
when String then matches_string?
when Regexp then matches_regexp?
when NilClass then matches_nil?
else fail ArgumentError, 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil'
end
end

def subscribe
@_subscribers << ActiveSupport::Notifications.subscribe(event_name) do |_name, _start, _finish, _id, payload|
serializer = payload[:serializer].name
serializers << serializer
end
end

def unsubscribe
@_subscribers.each do |subscriber|
ActiveSupport::Notifications.unsubscribe(subscriber)
end
end

private

def matches_class?
serializers.include?(expectation.name)
end

def matches_symbol?
camelize_expectation = expectation.to_s.camelize
serializers.include?(camelize_expectation)
end

def matches_string?
!expectation.empty? && serializers.include?(expectation)
end

def matches_regexp?
serializers.any? do |serializer|
serializer.match(expectation)
end
end

def matches_nil?
serializers.empty?
end

def a_serializer?
->(exp) { exp.is_a?(Class) && exp < ActiveModel::Serializer }
end

def event_name
::ActiveModelSerializers::Logging::RENDER_EVENT
end
end

private

def setup_serialization_subscriptions
@assert_serializer = AssertSerializer.new
@assert_serializer.subscribe
end

def teardown_serialization_subscriptions
@assert_serializer.unsubscribe
end
end
end
end
63 changes: 63 additions & 0 deletions test/active_model_serializers/test/serializer_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'test_helper'

module ActiveModelSerializers
module Test
class SerializerTest < ActionController::TestCase
include ActiveModelSerializers::Test::Serializer

class MyController < ActionController::Base
def render_using_serializer
render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
end

# For Rails4.0
def render_some_text
Rails.version > '4.1' ? render(plain: 'ok') : render(text: 'ok')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

womp womp (Really for Rails 5, but it works on 4.1+)

end
end

tests MyController

def test_supports_specifying_serializers_with_a_serializer_class
get :render_using_serializer
assert_serializer ProfileSerializer
end

def test_supports_specifying_serializers_with_a_regexp
get :render_using_serializer
assert_serializer(/\AProfile.+\Z/)
end

def test_supports_specifying_serializers_with_a_string
get :render_using_serializer
assert_serializer 'ProfileSerializer'
end

def test_supports_specifying_serializers_with_a_symbol
get :render_using_serializer
assert_serializer :profile_serializer
end

def test_supports_specifying_serializers_with_a_nil
get :render_some_text
assert_serializer nil
end

def test_raises_descriptive_error_message_when_serializer_was_not_rendered
get :render_using_serializer
e = assert_raise ActiveSupport::TestCase::Assertion do
assert_serializer 'PostSerializer'
end
assert_match 'expecting <"PostSerializer"> but rendering with <["ProfileSerializer"]>', e.message
end

def test_raises_argument_error_when_asserting_with_invalid_object
get :render_using_serializer
e = assert_raise ArgumentError do
assert_serializer Hash
end
assert_match 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil', e.message
end
end
end
end