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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -3
  3. data/Gemfile.lock +12 -41
  4. data/NEWS.md +118 -26
  5. data/README.md +34 -11
  6. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -0
  7. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +4 -0
  8. data/gemfiles/4.0.0.gemfile +2 -3
  9. data/gemfiles/4.0.0.gemfile.lock +47 -77
  10. data/gemfiles/4.0.1.gemfile +2 -3
  11. data/gemfiles/4.0.1.gemfile.lock +51 -79
  12. data/gemfiles/4.1.gemfile +2 -3
  13. data/gemfiles/4.1.gemfile.lock +73 -103
  14. data/gemfiles/4.2.gemfile +2 -3
  15. data/gemfiles/4.2.gemfile.lock +90 -124
  16. data/lib/shoulda/matchers.rb +1 -0
  17. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +6 -8
  18. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +1 -3
  19. data/lib/shoulda/matchers/action_controller/flash_store.rb +1 -8
  20. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +140 -88
  21. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +2 -5
  22. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +5 -10
  23. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +2 -4
  24. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -3
  25. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -5
  26. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -7
  27. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +35 -9
  28. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
  29. data/lib/shoulda/matchers/active_model.rb +57 -1
  30. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +2 -5
  31. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +162 -54
  32. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +5 -2
  33. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +1 -3
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +24 -11
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +4 -3
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +0 -2
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +4 -3
  38. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +2 -1
  39. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +15 -13
  40. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
  41. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +3 -3
  42. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +4 -4
  43. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +8 -8
  44. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +8 -8
  45. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +12 -14
  46. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +10 -4
  47. data/lib/shoulda/matchers/active_model/validation_matcher.rb +0 -3
  48. data/lib/shoulda/matchers/active_model/validator.rb +0 -8
  49. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -6
  50. data/lib/shoulda/matchers/active_record/association_matcher.rb +58 -43
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -2
  52. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +3 -5
  53. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +3 -5
  54. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -4
  55. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +3 -5
  56. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +7 -7
  57. data/lib/shoulda/matchers/doublespeak/double.rb +10 -1
  58. data/lib/shoulda/matchers/doublespeak/double_collection.rb +13 -5
  59. data/lib/shoulda/matchers/doublespeak/method_call.rb +10 -1
  60. data/lib/shoulda/matchers/doublespeak/object_double.rb +2 -1
  61. data/lib/shoulda/matchers/doublespeak/world.rb +10 -0
  62. data/lib/shoulda/matchers/error.rb +4 -0
  63. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +11 -10
  64. data/lib/shoulda/matchers/integrations/libraries.rb +1 -0
  65. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
  66. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +1 -1
  67. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +1 -1
  68. data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -1
  69. data/lib/shoulda/matchers/integrations/libraries/routing.rb +27 -0
  70. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
  71. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
  72. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
  73. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
  74. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +2 -2
  75. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
  76. data/lib/shoulda/matchers/routing.rb +10 -0
  77. data/lib/shoulda/matchers/version.rb +1 -1
  78. data/script/SUPPORTED_VERSIONS +1 -1
  79. data/spec/acceptance/independent_matchers_spec.rb +103 -42
  80. data/spec/doublespeak_spec_helper.rb +5 -1
  81. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +34 -11
  82. data/spec/support/acceptance/helpers/rspec_helpers.rb +9 -13
  83. data/spec/support/acceptance/helpers/step_helpers.rb +13 -0
  84. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  85. data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +1 -1
  86. data/spec/support/tests/command_runner.rb +5 -1
  87. data/spec/support/unit/helpers/active_record_versions.rb +0 -4
  88. data/spec/support/unit/shared_examples/set_session_or_flash.rb +8 -3
  89. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +198 -39
  90. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +269 -102
  91. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +24 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +118 -101
  93. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -82
  94. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +148 -121
  95. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +20 -8
  96. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +64 -183
  97. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +14 -0
  98. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +60 -0
  99. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +23 -7
  100. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +242 -0
  101. data/spec/unit_spec_helper.rb +4 -0
  102. data/tasks/documentation.rb +35 -0
  103. metadata +9 -8
  104. data/Guardfile +0 -5
  105. data/cucumber.yml +0 -1
  106. 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 Test::Unit /
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
- # # Test::Unit
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 Test::Unit, it provides a more expressive
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: 'sidebar') }
23
+ # it { should render_template(partial: '_sidebar') }
24
24
  # end
25
25
  # end
26
26
  #
27
- # # Test::Unit
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: 'sidebar')
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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`. For a Test::Unit suite, it
8
- # provides a more expressive syntax over `assert_routing`.
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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].now }
133
- # it { should set_flash[:foo].to('bar').now }
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
- # # Test::Unit
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].now
144
- # should set_flash[:foo].to('bar').now
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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 is used to test that an attribute of a model
5
- # can or cannot be set to a particular value or values. It is most
6
- # commonly used in conjunction with the `validates_format_of` validation.
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
- # #### should
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 allow_value('http://foo.com', 'http://bar.com/baz').
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
- # # Test::Unit
49
+ # # Minitest (Shoulda)
30
50
  # class UserProfileTest < ActiveSupport::TestCase
31
- # should allow_value('http://foo.com', 'http://bar.com/baz').
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
- # #### should_not
58
+ # #### Caveats
36
59
  #
37
- # In the negative form, `allow_value` asserts that an attribute cannot be
38
- # set to one or more values, succeeding if the *first* value causes the
39
- # record to be invalid.
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
- # **This can be surprising** so in this case if you need to check that
42
- # *all* of the values are invalid, use separate assertions:
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
- # class UserProfile
45
- # include ActiveModel::Model
46
- # attr_accessor :website_url
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
- # validates_format_of :website_url, with: URI.regexp
49
- # end
74
+ # class Foo
75
+ # include ActiveModel::Model
50
76
  #
51
- # describe UserProfile do
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
- # # Three assertions, all tested separately
56
- # it { should_not allow_value('fiz').for(:website_url) }
57
- # it { should_not allow_value('buz').for(:website_url) }
58
- # it { should_not allow_value('bar').for(:website_url) }
59
- # end
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- # # Test::Unit
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
- ensure_that_attribute_has_been_changed_to_or_from_nil!(value)
316
- rescue RangeError => exception
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 ensure_that_attribute_has_been_changed_to_or_from_nil!(expected_value)
436
+ def ensure_that_attribute_was_set!(expected_value)
329
437
  actual_value = instance.__send__(attribute_to_set)
330
438
 
331
- if expected_value.nil? != actual_value.nil?
439
+ if expected_value != actual_value && !ignoring_interference_by_writer?
332
440
  raise CouldNotSetAttributeError.create(
333
441
  instance.class,
334
442
  attribute_to_set,