Skip to content

Commit 9aed6ac

Browse files
committed
Merge pull request #1390 from bf4/maurogeorge-patch-02
Bring back assert_serializer for controller testing
2 parents d448481 + f5e2b99 commit 9aed6ac

File tree

9 files changed

+221
-1
lines changed

9 files changed

+221
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Features:
4545
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
4646
- [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that,
4747
when disabled, requires serializers to explicitly specified. (@trek)
48+
- [#1099](https://github.com/rails-api/active_model_serializers/pull/1099) Adds `assert_serializer` test helper (@maurogeorge)
4849

4950
Fixes:
5051
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10.
2222
- [How to add root key](howto/add_root_key.md)
2323
- [How to add pagination links](howto/add_pagination_links.md)
2424
- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md)
25+
- [Testing ActiveModelSerializers](howto/test.md)
2526

2627
## Integrations
2728

docs/howto/test.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# How to test
2+
3+
## Test helpers
4+
5+
ActiveModelSerializers provides a `assert_serializer` method to be used on your controller tests to
6+
assert that a specific serializer was used.
7+
8+
```ruby
9+
class PostsControllerTest < ActionController::TestCase
10+
test "should render post serializer" do
11+
get :index
12+
assert_serializer "PostSerializer"
13+
end
14+
end
15+
```
16+
17+
See [ActiveModelSerializers::Test::Serializer](../../lib/active_model_serializers/test/serializer.rb)
18+
for more examples and documentation.

lib/active_model/serializer/railtie.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ class Railtie < Rails::Railtie
1919
app.load_generators
2020
require 'generators/serializer/resource_override'
2121
end
22+
23+
if Rails.env.test?
24+
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
25+
end
2226
end
2327
end

lib/active_model_serializers.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def self.config
1414
autoload :Callbacks
1515
autoload :Deserialization
1616
autoload :Logging
17+
autoload :Test
1718
end
1819

1920
require 'active_model/serializer'

lib/active_model_serializers/logging.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#
66
module ActiveModelSerializers
77
module Logging
8+
RENDER_EVENT = 'render.active_model_serializers'.freeze
89
extend ActiveSupport::Concern
910

1011
included do
@@ -73,7 +74,7 @@ def notify(name, callback_name)
7374
end
7475

7576
def notify_render(*)
76-
event_name = 'render.active_model_serializers'.freeze
77+
event_name = RENDER_EVENT
7778
ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do
7879
yield
7980
end

lib/active_model_serializers/test.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module ActiveModelSerializers
2+
module Test
3+
extend ActiveSupport::Autoload
4+
autoload :Serializer
5+
end
6+
end
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
require 'set'
2+
module ActiveModelSerializers
3+
module Test
4+
module Serializer
5+
extend ActiveSupport::Concern
6+
7+
included do
8+
setup :setup_serialization_subscriptions
9+
teardown :teardown_serialization_subscriptions
10+
end
11+
12+
# Asserts that the request was rendered with the appropriate serializers.
13+
#
14+
# # assert that the "PostSerializer" serializer was rendered
15+
# assert_serializer "PostSerializer"
16+
#
17+
# # return a custom error message
18+
# assert_serializer "PostSerializer", "PostSerializer not rendered"
19+
#
20+
# # assert that the instance of PostSerializer was rendered
21+
# assert_serializer PostSerializer
22+
#
23+
# # assert that the "PostSerializer" serializer was rendered
24+
# assert_serializer :post_serializer
25+
#
26+
# # assert that the rendered serializer starts with "Post"
27+
# assert_serializer %r{\APost.+\Z}
28+
#
29+
# # assert that no serializer was rendered
30+
# assert_serializer nil
31+
#
32+
def assert_serializer(expectation, message = nil)
33+
@assert_serializer.expectation = expectation
34+
@assert_serializer.message = message
35+
@assert_serializer.response = response
36+
assert(@assert_serializer.matches?, @assert_serializer.message)
37+
end
38+
39+
class AssertSerializer
40+
attr_reader :serializers, :message
41+
attr_accessor :response, :expectation
42+
43+
def initialize
44+
@serializers = Set.new
45+
@_subscribers = []
46+
end
47+
48+
def message=(message)
49+
@message = message || "expecting <#{expectation.inspect}> but rendering with <#{serializers.to_a}>"
50+
end
51+
52+
def matches?
53+
# Force body to be read in case the template is being streamed.
54+
response.body
55+
56+
case expectation
57+
when a_serializer? then matches_class?
58+
when Symbol then matches_symbol?
59+
when String then matches_string?
60+
when Regexp then matches_regexp?
61+
when NilClass then matches_nil?
62+
else fail ArgumentError, 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil'
63+
end
64+
end
65+
66+
def subscribe
67+
@_subscribers << ActiveSupport::Notifications.subscribe(event_name) do |_name, _start, _finish, _id, payload|
68+
serializer = payload[:serializer].name
69+
serializers << serializer
70+
end
71+
end
72+
73+
def unsubscribe
74+
@_subscribers.each do |subscriber|
75+
ActiveSupport::Notifications.unsubscribe(subscriber)
76+
end
77+
end
78+
79+
private
80+
81+
def matches_class?
82+
serializers.include?(expectation.name)
83+
end
84+
85+
def matches_symbol?
86+
camelize_expectation = expectation.to_s.camelize
87+
serializers.include?(camelize_expectation)
88+
end
89+
90+
def matches_string?
91+
!expectation.empty? && serializers.include?(expectation)
92+
end
93+
94+
def matches_regexp?
95+
serializers.any? do |serializer|
96+
serializer.match(expectation)
97+
end
98+
end
99+
100+
def matches_nil?
101+
serializers.empty?
102+
end
103+
104+
def a_serializer?
105+
->(exp) { exp.is_a?(Class) && exp < ActiveModel::Serializer }
106+
end
107+
108+
def event_name
109+
::ActiveModelSerializers::Logging::RENDER_EVENT
110+
end
111+
end
112+
113+
private
114+
115+
def setup_serialization_subscriptions
116+
@assert_serializer = AssertSerializer.new
117+
@assert_serializer.subscribe
118+
end
119+
120+
def teardown_serialization_subscriptions
121+
@assert_serializer.unsubscribe
122+
end
123+
end
124+
end
125+
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require 'test_helper'
2+
3+
module ActiveModelSerializers
4+
module Test
5+
class SerializerTest < ActionController::TestCase
6+
include ActiveModelSerializers::Test::Serializer
7+
8+
class MyController < ActionController::Base
9+
def render_using_serializer
10+
render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
11+
end
12+
13+
# For Rails4.0
14+
def render_some_text
15+
Rails.version > '4.1' ? render(plain: 'ok') : render(text: 'ok')
16+
end
17+
end
18+
19+
tests MyController
20+
21+
def test_supports_specifying_serializers_with_a_serializer_class
22+
get :render_using_serializer
23+
assert_serializer ProfileSerializer
24+
end
25+
26+
def test_supports_specifying_serializers_with_a_regexp
27+
get :render_using_serializer
28+
assert_serializer(/\AProfile.+\Z/)
29+
end
30+
31+
def test_supports_specifying_serializers_with_a_string
32+
get :render_using_serializer
33+
assert_serializer 'ProfileSerializer'
34+
end
35+
36+
def test_supports_specifying_serializers_with_a_symbol
37+
get :render_using_serializer
38+
assert_serializer :profile_serializer
39+
end
40+
41+
def test_supports_specifying_serializers_with_a_nil
42+
get :render_some_text
43+
assert_serializer nil
44+
end
45+
46+
def test_raises_descriptive_error_message_when_serializer_was_not_rendered
47+
get :render_using_serializer
48+
e = assert_raise ActiveSupport::TestCase::Assertion do
49+
assert_serializer 'PostSerializer'
50+
end
51+
assert_match 'expecting <"PostSerializer"> but rendering with <["ProfileSerializer"]>', e.message
52+
end
53+
54+
def test_raises_argument_error_when_asserting_with_invalid_object
55+
get :render_using_serializer
56+
e = assert_raise ArgumentError do
57+
assert_serializer Hash
58+
end
59+
assert_match 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil', e.message
60+
end
61+
end
62+
end
63+
end

0 commit comments

Comments
 (0)