Skip to content

Commit

Permalink
Merge pull request #1390 from bf4/maurogeorge-patch-02
Browse files Browse the repository at this point in the history
Bring back assert_serializer for controller testing
  • Loading branch information
bf4 committed Jan 14, 2016
2 parents d448481 + f5e2b99 commit 9aed6ac
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 1 deletion.
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)

## 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
```

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
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')
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

0 comments on commit 9aed6ac

Please sign in to comment.