shoulda-matchers 3.0.0.rc1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/Gemfile.lock +12 -41
- data/NEWS.md +118 -26
- data/README.md +34 -11
- data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/style.css +4 -0
- data/gemfiles/4.0.0.gemfile +2 -3
- data/gemfiles/4.0.0.gemfile.lock +47 -77
- data/gemfiles/4.0.1.gemfile +2 -3
- data/gemfiles/4.0.1.gemfile.lock +51 -79
- data/gemfiles/4.1.gemfile +2 -3
- data/gemfiles/4.1.gemfile.lock +73 -103
- data/gemfiles/4.2.gemfile +2 -3
- data/gemfiles/4.2.gemfile.lock +90 -124
- data/lib/shoulda/matchers.rb +1 -0
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +6 -8
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +1 -3
- data/lib/shoulda/matchers/action_controller/flash_store.rb +1 -8
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +140 -88
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +2 -5
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +5 -10
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +2 -4
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -3
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -5
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -7
- data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +35 -9
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model.rb +57 -1
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +2 -5
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +162 -54
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +5 -2
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +1 -3
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +24 -11
- data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +4 -3
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +0 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +4 -3
- data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +2 -1
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +15 -13
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +4 -4
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +8 -8
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +8 -8
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +12 -14
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +10 -4
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +0 -3
- data/lib/shoulda/matchers/active_model/validator.rb +0 -8
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -6
- data/lib/shoulda/matchers/active_record/association_matcher.rb +58 -43
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +3 -5
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +3 -5
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -4
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +3 -5
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +7 -7
- data/lib/shoulda/matchers/doublespeak/double.rb +10 -1
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +13 -5
- data/lib/shoulda/matchers/doublespeak/method_call.rb +10 -1
- data/lib/shoulda/matchers/doublespeak/object_double.rb +2 -1
- data/lib/shoulda/matchers/doublespeak/world.rb +10 -0
- data/lib/shoulda/matchers/error.rb +4 -0
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +11 -10
- data/lib/shoulda/matchers/integrations/libraries.rb +1 -0
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/active_model.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/active_record.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -1
- data/lib/shoulda/matchers/integrations/libraries/routing.rb +27 -0
- data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +2 -2
- data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
- data/lib/shoulda/matchers/routing.rb +10 -0
- data/lib/shoulda/matchers/version.rb +1 -1
- data/script/SUPPORTED_VERSIONS +1 -1
- data/spec/acceptance/independent_matchers_spec.rb +103 -42
- data/spec/doublespeak_spec_helper.rb +5 -1
- data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +34 -11
- data/spec/support/acceptance/helpers/rspec_helpers.rb +9 -13
- data/spec/support/acceptance/helpers/step_helpers.rb +13 -0
- data/spec/support/acceptance/matchers/have_output.rb +1 -1
- data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +1 -1
- data/spec/support/tests/command_runner.rb +5 -1
- data/spec/support/unit/helpers/active_record_versions.rb +0 -4
- data/spec/support/unit/shared_examples/set_session_or_flash.rb +8 -3
- data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +198 -39
- data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +269 -102
- data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +24 -0
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +118 -101
- data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -82
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +148 -121
- data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +20 -8
- data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +64 -183
- data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +14 -0
- data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +60 -0
- data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +23 -7
- data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +242 -0
- data/spec/unit_spec_helper.rb +4 -0
- data/tasks/documentation.rb +35 -0
- metadata +9 -8
- data/Guardfile +0 -5
- data/cucumber.yml +0 -1
- data/lib/shoulda/matchers/active_model/validator_with_captured_range_error.rb +0 -12
@@ -3,7 +3,7 @@ module Shoulda
|
|
3
3
|
module ActionController
|
4
4
|
# The `redirect_to` matcher tests that an action redirects to a certain
|
5
5
|
# location. In a test suite using RSpec, it is very similar to
|
6
|
-
# rspec-rails's `redirect_to` matcher. In a test suite using
|
6
|
+
# rspec-rails's `redirect_to` matcher. In a test suite using Minitest +
|
7
7
|
# Shoulda, it provides a more expressive syntax over
|
8
8
|
# `assert_redirected_to`.
|
9
9
|
#
|
@@ -23,7 +23,7 @@ module Shoulda
|
|
23
23
|
# end
|
24
24
|
# end
|
25
25
|
#
|
26
|
-
# #
|
26
|
+
# # Minitest (Shoulda)
|
27
27
|
# class PostsControllerTest < ActionController::TestCase
|
28
28
|
# context 'GET #show' do
|
29
29
|
# setup { get :show }
|
@@ -43,9 +43,6 @@ module Shoulda
|
|
43
43
|
class RedirectToMatcher
|
44
44
|
attr_reader :failure_message, :failure_message_when_negated
|
45
45
|
|
46
|
-
alias failure_message_for_should failure_message
|
47
|
-
alias failure_message_for_should_not failure_message_when_negated
|
48
|
-
|
49
46
|
def initialize(url_or_description, context, &block)
|
50
47
|
@url_block = nil
|
51
48
|
@url = nil
|
@@ -3,8 +3,8 @@ module Shoulda
|
|
3
3
|
module ActionController
|
4
4
|
# The `render_template` matcher tests that an action renders a template
|
5
5
|
# or partial. In RSpec, it is very similar to rspec-rails's
|
6
|
-
# `render_template` matcher. In
|
7
|
-
# syntax over `assert_template`.
|
6
|
+
# `render_template` matcher. In a test suite using Minitest + Shoulda, it
|
7
|
+
# provides a more expressive syntax over `assert_template`.
|
8
8
|
#
|
9
9
|
# class PostsController < ApplicationController
|
10
10
|
# def show
|
@@ -20,22 +20,20 @@ module Shoulda
|
|
20
20
|
# before { get :show }
|
21
21
|
#
|
22
22
|
# it { should render_template('show') }
|
23
|
-
# it { should render_template(partial: '
|
23
|
+
# it { should render_template(partial: '_sidebar') }
|
24
24
|
# end
|
25
25
|
# end
|
26
26
|
#
|
27
|
-
# #
|
27
|
+
# # Minitest (Shoulda)
|
28
28
|
# class PostsControllerTest < ActionController::TestCase
|
29
29
|
# context 'GET #show' do
|
30
30
|
# setup { get :show }
|
31
31
|
#
|
32
32
|
# should render_template('show')
|
33
|
-
# should render_template(partial: '
|
33
|
+
# should render_template(partial: '_sidebar')
|
34
34
|
# end
|
35
35
|
# end
|
36
36
|
#
|
37
|
-
#
|
38
|
-
#
|
39
37
|
# @return [RenderTemplateMatcher]
|
40
38
|
#
|
41
39
|
def render_template(options = {}, message = nil)
|
@@ -46,9 +44,6 @@ module Shoulda
|
|
46
44
|
class RenderTemplateMatcher
|
47
45
|
attr_reader :failure_message, :failure_message_when_negated
|
48
46
|
|
49
|
-
alias failure_message_for_should failure_message
|
50
|
-
alias failure_message_for_should_not failure_message_when_negated
|
51
|
-
|
52
47
|
def initialize(options, message, context)
|
53
48
|
@options = options
|
54
49
|
@message = message
|
@@ -19,7 +19,7 @@ module Shoulda
|
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# #
|
22
|
+
# # Minitest (Shoulda)
|
23
23
|
# class PostsControllerTest < ActionController::TestCase
|
24
24
|
# context 'GET #show' do
|
25
25
|
# setup { get :show }
|
@@ -46,7 +46,7 @@ module Shoulda
|
|
46
46
|
# end
|
47
47
|
# end
|
48
48
|
#
|
49
|
-
# #
|
49
|
+
# # Minitest (Shoulda)
|
50
50
|
# class PostsControllerTest < ActionController::TestCase
|
51
51
|
# context 'GET #sidebar' do
|
52
52
|
# setup { get :sidebar }
|
@@ -88,12 +88,10 @@ module Shoulda
|
|
88
88
|
def failure_message
|
89
89
|
"Expected #{expectation}, but #{result}"
|
90
90
|
end
|
91
|
-
alias failure_message_for_should failure_message
|
92
91
|
|
93
92
|
def failure_message_when_negated
|
94
93
|
"Did not expect #{expectation}, but #{result}"
|
95
94
|
end
|
96
|
-
alias failure_message_for_should_not failure_message_when_negated
|
97
95
|
|
98
96
|
def description
|
99
97
|
description = 'render with '
|
@@ -23,7 +23,7 @@ module Shoulda
|
|
23
23
|
# end
|
24
24
|
# end
|
25
25
|
#
|
26
|
-
# #
|
26
|
+
# # Minitest (Shoulda)
|
27
27
|
# class ApplicationControllerTest < ActionController::TestCase
|
28
28
|
# should rescue_from(ActiveRecord::RecordNotFound).
|
29
29
|
# with(:handle_not_found)
|
@@ -62,12 +62,10 @@ module Shoulda
|
|
62
62
|
def failure_message
|
63
63
|
"Expected #{expectation}"
|
64
64
|
end
|
65
|
-
alias failure_message_for_should failure_message
|
66
65
|
|
67
66
|
def failure_message_when_negated
|
68
67
|
"Did not expect #{expectation}"
|
69
68
|
end
|
70
|
-
alias failure_message_for_should_not failure_message_when_negated
|
71
69
|
|
72
70
|
protected
|
73
71
|
|
@@ -21,7 +21,7 @@ module Shoulda
|
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
24
|
-
# #
|
24
|
+
# # Minitest (Shoulda)
|
25
25
|
# class PostsControllerTest < ActionController::TestCase
|
26
26
|
# context 'GET #index' do
|
27
27
|
# setup { get :index }
|
@@ -47,7 +47,7 @@ module Shoulda
|
|
47
47
|
# end
|
48
48
|
# end
|
49
49
|
#
|
50
|
-
# #
|
50
|
+
# # Minitest (Shoulda)
|
51
51
|
# class PostsControllerTest < ActionController::TestCase
|
52
52
|
# context 'DELETE #destroy' do
|
53
53
|
# setup { delete :destroy }
|
@@ -73,7 +73,7 @@ module Shoulda
|
|
73
73
|
# end
|
74
74
|
# end
|
75
75
|
#
|
76
|
-
# #
|
76
|
+
# # Minitest (Shoulda)
|
77
77
|
# class PostsControllerTest < ActionController::TestCase
|
78
78
|
# context 'GET #show' do
|
79
79
|
# setup { get :show }
|
@@ -102,12 +102,10 @@ module Shoulda
|
|
102
102
|
def failure_message
|
103
103
|
"Expected #{expectation}"
|
104
104
|
end
|
105
|
-
alias failure_message_for_should failure_message
|
106
105
|
|
107
106
|
def failure_message_when_negated
|
108
107
|
"Did not expect #{expectation}"
|
109
108
|
end
|
110
|
-
alias failure_message_for_should_not failure_message_when_negated
|
111
109
|
|
112
110
|
def description
|
113
111
|
"respond with #{@status}"
|
@@ -4,8 +4,9 @@ module Shoulda
|
|
4
4
|
# The `route` matcher tests that a route resolves to a controller,
|
5
5
|
# action, and params; and that the controller, action, and params
|
6
6
|
# generates the same route. For an RSpec suite, this is like using a
|
7
|
-
# combination of `route_to` and `be_routable`.
|
8
|
-
# provides a more expressive syntax over
|
7
|
+
# combination of `route_to` and `be_routable`. In a test suite using
|
8
|
+
# Minitest + Shoulda, it provides a more expressive syntax over
|
9
|
+
# `assert_routing`.
|
9
10
|
#
|
10
11
|
# You can use this matcher either in a controller test case or in a
|
11
12
|
# routing test case. For instance, given these routes:
|
@@ -28,7 +29,7 @@ module Shoulda
|
|
28
29
|
# it { should route(:get, '/posts/1').to(action: :show, id: 1) }
|
29
30
|
# end
|
30
31
|
#
|
31
|
-
# #
|
32
|
+
# # Minitest (Shoulda)
|
32
33
|
# class PostsControllerTest < ActionController::TestCase
|
33
34
|
# should route(:get, '/posts').to(action: 'index')
|
34
35
|
# should route(:get, '/posts/1').to(action: :show, id: 1)
|
@@ -49,7 +50,7 @@ module Shoulda
|
|
49
50
|
# end
|
50
51
|
# end
|
51
52
|
#
|
52
|
-
# #
|
53
|
+
# # Minitest (Shoulda)
|
53
54
|
# class RoutesTest < ActionController::IntegrationTest
|
54
55
|
# should route(:get, '/posts').
|
55
56
|
# to(controller: :posts, action: :index)
|
@@ -99,9 +100,6 @@ module Shoulda
|
|
99
100
|
|
100
101
|
attr_reader :failure_message, :failure_message_when_negated
|
101
102
|
|
102
|
-
alias failure_message_for_should failure_message
|
103
|
-
alias failure_message_for_should_not failure_message_when_negated
|
104
|
-
|
105
103
|
def to(*args)
|
106
104
|
@params = RouteParams.new(args).normalize
|
107
105
|
self
|
@@ -30,7 +30,7 @@ module Shoulda
|
|
30
30
|
# end
|
31
31
|
# end
|
32
32
|
#
|
33
|
-
# #
|
33
|
+
# # Minitest (Shoulda)
|
34
34
|
# class PostsControllerTest < ActionController::TestCase
|
35
35
|
# context 'GET #index' do
|
36
36
|
# setup { get :index }
|
@@ -67,7 +67,7 @@ module Shoulda
|
|
67
67
|
# end
|
68
68
|
# end
|
69
69
|
#
|
70
|
-
# #
|
70
|
+
# # Minitest (Shoulda)
|
71
71
|
# class PostsControllerTest < ActionController::TestCase
|
72
72
|
# context 'GET #index' do
|
73
73
|
# setup { get :show }
|
@@ -100,7 +100,7 @@ module Shoulda
|
|
100
100
|
# end
|
101
101
|
# end
|
102
102
|
#
|
103
|
-
# #
|
103
|
+
# # Minitest (Shoulda)
|
104
104
|
# class PostsControllerTest < ActionController::TestCase
|
105
105
|
# context 'GET #index' do
|
106
106
|
# setup { get :show }
|
@@ -129,19 +129,19 @@ module Shoulda
|
|
129
129
|
# before { get :show }
|
130
130
|
#
|
131
131
|
# it { should set_flash.now }
|
132
|
-
# it { should set_flash[:foo]
|
133
|
-
# it { should set_flash[:foo].to('bar')
|
132
|
+
# it { should set_flash.now[:foo] }
|
133
|
+
# it { should set_flash.now[:foo].to('bar') }
|
134
134
|
# end
|
135
135
|
# end
|
136
136
|
#
|
137
|
-
# #
|
137
|
+
# # Minitest (Shoulda)
|
138
138
|
# class PostsControllerTest < ActionController::TestCase
|
139
139
|
# context 'GET #index' do
|
140
140
|
# setup { get :show }
|
141
141
|
#
|
142
142
|
# should set_flash.now
|
143
|
-
# should set_flash[:foo]
|
144
|
-
# should set_flash[:foo].to('bar')
|
143
|
+
# should set_flash.now[:foo]
|
144
|
+
# should set_flash.now[:foo].to('bar')
|
145
145
|
# end
|
146
146
|
# end
|
147
147
|
#
|
@@ -173,6 +173,10 @@ module Shoulda
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def now
|
176
|
+
if key || expected_value
|
177
|
+
raise QualifierOrderError
|
178
|
+
end
|
179
|
+
|
176
180
|
store = FlashStore.now
|
177
181
|
@underlying_matcher = SetSessionOrFlashMatcher.new(store)
|
178
182
|
self
|
@@ -184,18 +188,40 @@ module Shoulda
|
|
184
188
|
end
|
185
189
|
|
186
190
|
def [](key)
|
191
|
+
@key = key
|
187
192
|
underlying_matcher[key]
|
188
193
|
self
|
189
194
|
end
|
190
195
|
|
191
196
|
def to(expected_value = nil, &block)
|
197
|
+
@expected_value = expected_value
|
192
198
|
underlying_matcher.to(expected_value, &block)
|
193
199
|
self
|
194
200
|
end
|
195
201
|
|
196
202
|
protected
|
197
203
|
|
198
|
-
attr_reader :underlying_matcher
|
204
|
+
attr_reader :underlying_matcher, :key, :expected_value
|
205
|
+
|
206
|
+
# @private
|
207
|
+
class QualifierOrderError < StandardError
|
208
|
+
def message
|
209
|
+
<<-MESSAGE.strip
|
210
|
+
Using `set_flash` with the `now` qualifier and specifying `now` after other
|
211
|
+
qualifiers is no longer allowed.
|
212
|
+
|
213
|
+
You'll want to use `now` immediately after `set_flash`. For instance:
|
214
|
+
|
215
|
+
# Valid
|
216
|
+
should set_flash.now[:foo]
|
217
|
+
should set_flash.now[:foo].to('bar')
|
218
|
+
|
219
|
+
# Invalid
|
220
|
+
should set_flash[:foo].now
|
221
|
+
should set_flash[:foo].to('bar').now
|
222
|
+
MESSAGE
|
223
|
+
end
|
224
|
+
end
|
199
225
|
end
|
200
226
|
end
|
201
227
|
end
|
@@ -30,7 +30,7 @@ module Shoulda
|
|
30
30
|
# end
|
31
31
|
# end
|
32
32
|
#
|
33
|
-
# #
|
33
|
+
# # Minitest (Shoulda)
|
34
34
|
# class PostsControllerTest < ActionController::TestCase
|
35
35
|
# context 'GET #index' do
|
36
36
|
# setup { get :index }
|
@@ -67,7 +67,7 @@ module Shoulda
|
|
67
67
|
# end
|
68
68
|
# end
|
69
69
|
#
|
70
|
-
# #
|
70
|
+
# # Minitest (Shoulda)
|
71
71
|
# class PostsControllerTest < ActionController::TestCase
|
72
72
|
# context 'GET #index' do
|
73
73
|
# setup { get :show }
|
@@ -100,7 +100,7 @@ module Shoulda
|
|
100
100
|
# end
|
101
101
|
# end
|
102
102
|
#
|
103
|
-
# #
|
103
|
+
# # Minitest (Shoulda)
|
104
104
|
# class PostsControllerTest < ActionController::TestCase
|
105
105
|
# context 'GET #index' do
|
106
106
|
# setup { get :show }
|
@@ -2,7 +2,6 @@ require 'shoulda/matchers/active_model/helpers'
|
|
2
2
|
require 'shoulda/matchers/active_model/validation_matcher'
|
3
3
|
require 'shoulda/matchers/active_model/validator'
|
4
4
|
require 'shoulda/matchers/active_model/strict_validator'
|
5
|
-
require 'shoulda/matchers/active_model/validator_with_captured_range_error'
|
6
5
|
require 'shoulda/matchers/active_model/allow_value_matcher'
|
7
6
|
require 'shoulda/matchers/active_model/disallow_value_matcher'
|
8
7
|
require 'shoulda/matchers/active_model/validate_length_of_matcher'
|
@@ -24,6 +23,63 @@ require 'shoulda/matchers/active_model/have_secure_password_matcher'
|
|
24
23
|
|
25
24
|
module Shoulda
|
26
25
|
module Matchers
|
26
|
+
# This mixin provides matchers that are used to test behavior, such as
|
27
|
+
# validations, that you've added to your ActiveModel (or ActiveRecord)
|
28
|
+
# objects.
|
29
|
+
#
|
30
|
+
# ### Testing conditional validations
|
31
|
+
#
|
32
|
+
# If your model defines a validation conditionally -- meaning that the
|
33
|
+
# validation is declared with an `:if` or `:unless` option -- how do you
|
34
|
+
# test it? You might expect the validation matchers here to have
|
35
|
+
# corresponding `if` or `unless` qualifiers, but this isn't what you use.
|
36
|
+
# Instead, before using the matcher in question, you place the record
|
37
|
+
# you're testing in a state such that the validation you're also testing
|
38
|
+
# will be run. A common way to do this is to make a new `context` and
|
39
|
+
# override the subject to populate the record accordingly. You'll also want
|
40
|
+
# to make sure to test that the validation is *not* run when the
|
41
|
+
# conditional fails.
|
42
|
+
#
|
43
|
+
# Here's an example to illustrate what we mean:
|
44
|
+
#
|
45
|
+
# class User
|
46
|
+
# include ActiveModel::Model
|
47
|
+
#
|
48
|
+
# attr_accessor :role, :admin
|
49
|
+
#
|
50
|
+
# validates_presence_of :role, if: :admin
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # RSpec
|
54
|
+
# describe User do
|
55
|
+
# context "when an admin" do
|
56
|
+
# subject { User.new(admin: true) }
|
57
|
+
#
|
58
|
+
# it { should validate_presence_of(:role) }
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# context "when not an admin" do
|
62
|
+
# subject { User.new(admin: false) }
|
63
|
+
#
|
64
|
+
# it { should_not validate_presence_of(:role) }
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# # Minitest (Shoulda)
|
69
|
+
# class UserTest < ActiveSupport::TestCase
|
70
|
+
# context "when an admin" do
|
71
|
+
# subject { User.new(admin: true) }
|
72
|
+
#
|
73
|
+
# should validate_presence_of(:role)
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# context "when not an admin" do
|
77
|
+
# subject { User.new(admin: false) }
|
78
|
+
#
|
79
|
+
# should_not validate_presence_of(:role)
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
#
|
27
83
|
module ActiveModel
|
28
84
|
end
|
29
85
|
end
|
@@ -31,7 +31,7 @@ module Shoulda
|
|
31
31
|
# it { should_not allow_mass_assignment_of(:encrypted_password) }
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
# #
|
34
|
+
# # Minitest (Shoulda)
|
35
35
|
# class PostTest < ActiveSupport::TestCase
|
36
36
|
# should allow_mass_assignment_of(:title)
|
37
37
|
# end
|
@@ -60,7 +60,7 @@ module Shoulda
|
|
60
60
|
# it { should allow_mass_assignment_of(:title).as(:admin) }
|
61
61
|
# end
|
62
62
|
#
|
63
|
-
# #
|
63
|
+
# # Minitest (Shoulda)
|
64
64
|
# class PostTest < ActiveSupport::TestCase
|
65
65
|
# should allow_mass_assignment_of(:title).as(:admin)
|
66
66
|
# end
|
@@ -75,9 +75,6 @@ module Shoulda
|
|
75
75
|
class AllowMassAssignmentOfMatcher
|
76
76
|
attr_reader :failure_message, :failure_message_when_negated
|
77
77
|
|
78
|
-
alias failure_message_for_should failure_message
|
79
|
-
alias failure_message_for_should_not failure_message_when_negated
|
80
|
-
|
81
78
|
def initialize(attribute)
|
82
79
|
@attribute = attribute.to_s
|
83
80
|
@options = {}
|
@@ -1,15 +1,11 @@
|
|
1
1
|
module Shoulda
|
2
2
|
module Matchers
|
3
3
|
module ActiveModel
|
4
|
-
# The `allow_value` matcher
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# The `allow_value` matcher (or its alias, `allow_values`) is used to
|
5
|
+
# ensure that an attribute is valid or invalid if set to one or more
|
6
|
+
# values.
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# In the positive form, `allow_value` asserts that an attribute can be
|
11
|
-
# set to one or more values, succeeding if none of the values cause the
|
12
|
-
# record to be invalid:
|
8
|
+
# Take this model for example:
|
13
9
|
#
|
14
10
|
# class UserProfile
|
15
11
|
# include ActiveModel::Model
|
@@ -18,45 +14,136 @@ module Shoulda
|
|
18
14
|
# validates_format_of :website_url, with: URI.regexp
|
19
15
|
# end
|
20
16
|
#
|
17
|
+
# You can use `allow_value` to test one value at a time:
|
18
|
+
#
|
19
|
+
# # RSpec
|
20
|
+
# describe UserProfile do
|
21
|
+
# it { should allow_value('http://foo.com').for(:website_url) }
|
22
|
+
# it { should allow_value('http://bar.com').for(:website_url) }
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # Minitest (Shoulda)
|
26
|
+
# class UserProfileTest < ActiveSupport::TestCase
|
27
|
+
# should allow_value('http://foo.com').for(:website_url)
|
28
|
+
# should allow_value('http://bar.com').for(:website_url)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# You can also test multiple values in one go, if you like. In the
|
32
|
+
# positive sense, this makes an assertion that none of the values cause the
|
33
|
+
# record to be invalid. In the negative sense, this makes an assertion
|
34
|
+
# that none of the values cause the record to be valid:
|
35
|
+
#
|
21
36
|
# # RSpec
|
22
37
|
# describe UserProfile do
|
23
38
|
# it do
|
24
|
-
# should
|
39
|
+
# should allow_values('http://foo.com', 'http://bar.com').
|
40
|
+
# for(:website_url)
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# it do
|
44
|
+
# should_not allow_values('http://foo.com', 'buz').
|
25
45
|
# for(:website_url)
|
26
46
|
# end
|
27
47
|
# end
|
28
48
|
#
|
29
|
-
# #
|
49
|
+
# # Minitest (Shoulda)
|
30
50
|
# class UserProfileTest < ActiveSupport::TestCase
|
31
|
-
# should
|
51
|
+
# should allow_values('http://foo.com', 'http://bar.com/baz').
|
52
|
+
# for(:website_url)
|
53
|
+
#
|
54
|
+
# should_not allow_values('http://foo.com', 'buz').
|
32
55
|
# for(:website_url)
|
33
56
|
# end
|
34
57
|
#
|
35
|
-
# ####
|
58
|
+
# #### Caveats
|
36
59
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
60
|
+
# When using `allow_value` or any matchers that depend on it, you may
|
61
|
+
# encounter a CouldNotSetAttributeError. This exception is raised if the
|
62
|
+
# matcher, in attempting to set a value on the attribute, detects that
|
63
|
+
# the value set is different from the value that the attribute returns
|
64
|
+
# upon reading it back.
|
40
65
|
#
|
41
|
-
#
|
42
|
-
#
|
66
|
+
# This usually happens if the writer method (`foo=`, `bar=`, etc.) for
|
67
|
+
# that attribute has custom logic to ignore certain incoming values or
|
68
|
+
# change them in any way. Here are three examples we've seen:
|
43
69
|
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
70
|
+
# * You're attempting to assert that an attribute should not allow nil,
|
71
|
+
# yet the attribute's writer method contains a conditional to do nothing
|
72
|
+
# if the attribute is set to nil:
|
47
73
|
#
|
48
|
-
#
|
49
|
-
#
|
74
|
+
# class Foo
|
75
|
+
# include ActiveModel::Model
|
50
76
|
#
|
51
|
-
#
|
52
|
-
# # One assertion: 'buz' and 'bar' will not be tested
|
53
|
-
# it { should_not allow_value('fiz', 'buz', 'bar').for(:website_url) }
|
77
|
+
# attr_reader :bar
|
54
78
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
79
|
+
# def bar=(value)
|
80
|
+
# return if value.nil?
|
81
|
+
# @bar = value
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# describe Foo do
|
86
|
+
# it do
|
87
|
+
# foo = Foo.new
|
88
|
+
# foo.bar = "baz"
|
89
|
+
# # This will raise a CouldNotSetAttributeError since `foo.bar` is now "123"
|
90
|
+
# expect(foo).not_to allow_value(nil).for(:bar)
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# * You're attempting to assert that an numeric attribute should not allow a
|
95
|
+
# string that contains non-numeric characters, yet the writer method for
|
96
|
+
# that attribute strips out non-numeric characters:
|
97
|
+
#
|
98
|
+
# class Foo
|
99
|
+
# include ActiveModel::Model
|
100
|
+
#
|
101
|
+
# attr_reader :bar
|
102
|
+
#
|
103
|
+
# def bar=(value)
|
104
|
+
# @bar = value.gsub(/\D+/, '')
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# describe Foo do
|
109
|
+
# it do
|
110
|
+
# foo = Foo.new
|
111
|
+
# # This will raise a CouldNotSetAttributeError since `foo.bar` is now "123"
|
112
|
+
# expect(foo).not_to allow_value("abc123").for(:bar)
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# * You're passing a value to `allow_value` that the model typecasts into
|
117
|
+
# another value:
|
118
|
+
#
|
119
|
+
# describe Foo do
|
120
|
+
# # Assume that `attr` is a string
|
121
|
+
# # This will raise a CouldNotSetAttributeError since `attr` typecasts `[]` to `"[]"`
|
122
|
+
# it { should_not allow_value([]).for(:attr) }
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# So when you encounter this exception, you have a couple of options:
|
126
|
+
#
|
127
|
+
# * If you understand the problem and wish to override this behavior to
|
128
|
+
# get around this exception, you can add the
|
129
|
+
# `ignoring_interference_by_writer` qualifier like so:
|
130
|
+
#
|
131
|
+
# it do
|
132
|
+
# should_not allow_value([]).
|
133
|
+
# for(:attr).
|
134
|
+
# ignoring_interference_by_writer
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# * Note, however, that the above option will not always cause the test to
|
138
|
+
# pass. In this case, this is telling you that you don't need to use
|
139
|
+
# `allow_value`, or quite possibly even the validation that you're
|
140
|
+
# testing altogether. In any case, we would probably make the argument
|
141
|
+
# that since it's clear that something is responsible for sanitizing
|
142
|
+
# incoming data before it's stored in your model, there's no need to
|
143
|
+
# ensure that sanitization places the model in a valid state, if such
|
144
|
+
# sanitization creates valid data. In terms of testing, the sanitization
|
145
|
+
# code should probably be tested, but not the effects of that
|
146
|
+
# sanitization on the validness of the model.
|
60
147
|
#
|
61
148
|
# #### Qualifiers
|
62
149
|
#
|
@@ -82,7 +169,7 @@ module Shoulda
|
|
82
169
|
# end
|
83
170
|
# end
|
84
171
|
#
|
85
|
-
# #
|
172
|
+
# # Minitest (Shoulda)
|
86
173
|
# class UserProfileTest < ActiveSupport::TestCase
|
87
174
|
# should allow_value('2013-01-01').
|
88
175
|
# for(:birthday_as_string).
|
@@ -111,7 +198,7 @@ module Shoulda
|
|
111
198
|
# end
|
112
199
|
# end
|
113
200
|
#
|
114
|
-
# #
|
201
|
+
# # Minitest (Shoulda)
|
115
202
|
# class UserProfileTest < ActiveSupport::TestCase
|
116
203
|
# should allow_value('open', 'closed').
|
117
204
|
# for(:state).
|
@@ -138,7 +225,7 @@ module Shoulda
|
|
138
225
|
# end
|
139
226
|
# end
|
140
227
|
#
|
141
|
-
# #
|
228
|
+
# # Minitest (Shoulda)
|
142
229
|
# class UserProfileTest < ActiveSupport::TestCase
|
143
230
|
# should allow_value('open', 'closed').
|
144
231
|
# for(:state).
|
@@ -176,7 +263,7 @@ module Shoulda
|
|
176
263
|
# end
|
177
264
|
# end
|
178
265
|
#
|
179
|
-
# #
|
266
|
+
# # Minitest (Shoulda)
|
180
267
|
# class UserProfileTest < ActiveSupport::TestCase
|
181
268
|
# should allow_value('Broncos', 'Titans').
|
182
269
|
# for(:sports_team).
|
@@ -185,6 +272,32 @@ module Shoulda
|
|
185
272
|
# )
|
186
273
|
# end
|
187
274
|
#
|
275
|
+
# ##### ignoring_interference_by_writer
|
276
|
+
#
|
277
|
+
# Use `ignoring_interference_by_writer` if you've encountered a
|
278
|
+
# CouldNotSetAttributeError and wish to ignore it. Please read the Caveats
|
279
|
+
# section above for more information.
|
280
|
+
#
|
281
|
+
# class Address < ActiveRecord::Base
|
282
|
+
# # Address has a zip_code field which is a string
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
# # RSpec
|
286
|
+
# describe Address do
|
287
|
+
# it do
|
288
|
+
# should_not allow_value([]).
|
289
|
+
# for(:zip_code).
|
290
|
+
# ignoring_interference_by_writer
|
291
|
+
# end
|
292
|
+
# end
|
293
|
+
#
|
294
|
+
# # Minitest (Shoulda)
|
295
|
+
# class AddressTest < ActiveSupport::TestCase
|
296
|
+
# should_not allow_value([]).
|
297
|
+
# for(:zip_code).
|
298
|
+
# ignoring_interference_by_writer
|
299
|
+
# end
|
300
|
+
#
|
188
301
|
# @return [AllowValueMatcher]
|
189
302
|
#
|
190
303
|
def allow_value(*values)
|
@@ -194,6 +307,7 @@ module Shoulda
|
|
194
307
|
AllowValueMatcher.new(*values)
|
195
308
|
end
|
196
309
|
end
|
310
|
+
# @private
|
197
311
|
alias_method :allow_values, :allow_value
|
198
312
|
|
199
313
|
# @private
|
@@ -226,6 +340,7 @@ module Shoulda
|
|
226
340
|
self.options = {}
|
227
341
|
self.after_setting_value_callback = -> {}
|
228
342
|
self.validator = Validator.new
|
343
|
+
@ignoring_interference_by_writer = false
|
229
344
|
end
|
230
345
|
|
231
346
|
def for(attribute)
|
@@ -255,6 +370,11 @@ module Shoulda
|
|
255
370
|
self
|
256
371
|
end
|
257
372
|
|
373
|
+
def ignoring_interference_by_writer
|
374
|
+
@ignoring_interference_by_writer = true
|
375
|
+
self
|
376
|
+
end
|
377
|
+
|
258
378
|
def _after_setting_value(&callback)
|
259
379
|
self.after_setting_value_callback = callback
|
260
380
|
end
|
@@ -272,12 +392,10 @@ module Shoulda
|
|
272
392
|
def failure_message
|
273
393
|
"Did not expect #{expectation},\ngot#{error_description}"
|
274
394
|
end
|
275
|
-
alias failure_message_for_should failure_message
|
276
395
|
|
277
396
|
def failure_message_when_negated
|
278
397
|
"Expected #{expectation},\ngot#{error_description}"
|
279
398
|
end
|
280
|
-
alias failure_message_for_should_not failure_message_when_negated
|
281
399
|
|
282
400
|
def description
|
283
401
|
validator.allow_description(allowed_values)
|
@@ -299,6 +417,10 @@ module Shoulda
|
|
299
417
|
validator.attribute = attribute
|
300
418
|
end
|
301
419
|
|
420
|
+
def ignoring_interference_by_writer?
|
421
|
+
@ignoring_interference_by_writer
|
422
|
+
end
|
423
|
+
|
302
424
|
def value_matches?(value)
|
303
425
|
self.value = value
|
304
426
|
set_attribute(value)
|
@@ -306,29 +428,15 @@ module Shoulda
|
|
306
428
|
end
|
307
429
|
|
308
430
|
def set_attribute(value)
|
309
|
-
set_attribute_ignoring_range_errors(value)
|
310
|
-
after_setting_value_callback.call
|
311
|
-
end
|
312
|
-
|
313
|
-
def set_attribute_ignoring_range_errors(value)
|
314
431
|
instance.__send__("#{attribute_to_set}=", value)
|
315
|
-
|
316
|
-
|
317
|
-
# Have to reset the attribute so that we don't get a RangeError the
|
318
|
-
# next time we attempt to write the attribute (ActiveRecord seems to
|
319
|
-
# set the attribute to the "bad" value anyway)
|
320
|
-
reset_attribute
|
321
|
-
validator.capture_range_error(exception)
|
322
|
-
end
|
323
|
-
|
324
|
-
def reset_attribute
|
325
|
-
instance.send(:raw_write_attribute, attribute_to_set, nil)
|
432
|
+
ensure_that_attribute_was_set!(value)
|
433
|
+
after_setting_value_callback.call
|
326
434
|
end
|
327
435
|
|
328
|
-
def
|
436
|
+
def ensure_that_attribute_was_set!(expected_value)
|
329
437
|
actual_value = instance.__send__(attribute_to_set)
|
330
438
|
|
331
|
-
if expected_value
|
439
|
+
if expected_value != actual_value && !ignoring_interference_by_writer?
|
332
440
|
raise CouldNotSetAttributeError.create(
|
333
441
|
instance.class,
|
334
442
|
attribute_to_set,
|