shoulda 2.10.1 → 2.10.2
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.
- data/CONTRIBUTION_GUIDELINES.rdoc +4 -6
- data/README.rdoc +14 -12
- data/lib/shoulda.rb +1 -1
- data/lib/shoulda/action_controller.rb +0 -2
- data/lib/shoulda/action_controller/macros.rb +38 -75
- data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +4 -0
- data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +1 -1
- data/lib/shoulda/action_view/macros.rb +6 -1
- data/lib/shoulda/active_record/assertions.rb +4 -4
- data/lib/shoulda/active_record/helpers.rb +0 -13
- data/lib/shoulda/active_record/macros.rb +50 -127
- data/lib/shoulda/active_record/matchers.rb +2 -1
- data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +1 -1
- data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +5 -5
- data/lib/shoulda/active_record/matchers/association_matcher.rb +3 -3
- data/lib/shoulda/active_record/matchers/{have_index_matcher.rb → have_db_index_matcher.rb} +15 -8
- data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +3 -0
- data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +67 -0
- data/lib/shoulda/active_record/matchers/validation_matcher.rb +1 -0
- data/lib/shoulda/assertions.rb +18 -6
- data/lib/shoulda/context.rb +99 -1
- data/lib/shoulda/macros.rb +83 -23
- data/lib/shoulda/private_helpers.rb +1 -8
- data/lib/shoulda/test_unit.rb +3 -0
- data/test/fail_macros.rb +6 -1
- data/test/functional/posts_controller_test.rb +17 -21
- data/test/functional/users_controller_test.rb +1 -1
- data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/allow_value_matcher_test.rb +24 -1
- data/test/matchers/active_record/association_matcher_test.rb +8 -3
- data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/ensure_length_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/have_db_column_matcher_test.rb +1 -1
- data/test/matchers/active_record/{have_index_matcher_test.rb → have_db_index_matcher_test.rb} +24 -7
- data/test/matchers/active_record/have_named_scope_matcher_test.rb +1 -1
- data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +1 -1
- data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/validate_format_of_matcher_test.rb +39 -0
- data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/validate_presence_of_matcher_test.rb +1 -1
- data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +1 -1
- data/test/matchers/controller/assign_to_matcher_test.rb +1 -1
- data/test/matchers/controller/filter_param_matcher_test.rb +1 -1
- data/test/matchers/controller/render_with_layout_matcher_test.rb +1 -1
- data/test/matchers/controller/respond_with_content_type_matcher_test.rb +12 -7
- data/test/matchers/controller/respond_with_matcher_test.rb +1 -1
- data/test/matchers/controller/route_matcher_test.rb +1 -1
- data/test/matchers/controller/set_session_matcher_test.rb +9 -2
- data/test/matchers/controller/set_the_flash_matcher.rb +1 -1
- data/test/model_builder.rb +1 -1
- data/test/other/autoload_macro_test.rb +1 -1
- data/test/other/context_test.rb +45 -1
- data/test/other/convert_to_should_syntax_test.rb +3 -3
- data/test/other/helpers_test.rb +102 -3
- data/test/other/private_helpers_test.rb +6 -8
- data/test/other/should_test.rb +8 -3
- data/test/rails_root/app/controllers/{application.rb → application_controller.rb} +0 -0
- data/test/rails_root/app/controllers/posts_controller.rb +1 -0
- data/test/rails_root/app/models/pets/cat.rb +7 -0
- data/test/rails_root/app/models/profile.rb +2 -0
- data/test/rails_root/app/models/registration.rb +2 -0
- data/test/rails_root/app/models/user.rb +3 -0
- data/test/rails_root/config/boot.rb +6 -5
- data/test/rails_root/config/environment.rb +5 -1
- data/test/rails_root/db/migrate/20090506203502_create_profiles.rb +12 -0
- data/test/rails_root/db/migrate/20090506203536_create_registrations.rb +14 -0
- data/test/rails_root/db/migrate/20090513104502_create_cats.rb +12 -0
- data/test/rails_root/test/shoulda_macros/custom_macro.rb +1 -1
- data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +1 -1
- data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +1 -1
- data/test/rspec_test.rb +1 -1
- data/test/test_helper.rb +3 -10
- data/test/unit/address_test.rb +2 -2
- data/test/unit/cat_test.rb +7 -0
- data/test/unit/dog_test.rb +2 -3
- data/test/unit/flea_test.rb +1 -1
- data/test/unit/post_test.rb +2 -2
- data/test/unit/product_test.rb +2 -6
- data/test/unit/tag_test.rb +2 -2
- data/test/unit/tagging_test.rb +1 -1
- data/test/unit/user_test.rb +18 -8
- metadata +15 -9
- data/lib/shoulda/action_controller/helpers.rb +0 -47
- data/test/rails_root/log/sqlite3.log +0 -0
- data/test/rails_root/log/test.log +0 -0
@@ -4,12 +4,13 @@ require 'shoulda/active_record/matchers/allow_value_matcher'
|
|
4
4
|
require 'shoulda/active_record/matchers/ensure_length_of_matcher'
|
5
5
|
require 'shoulda/active_record/matchers/ensure_inclusion_of_matcher'
|
6
6
|
require 'shoulda/active_record/matchers/validate_presence_of_matcher'
|
7
|
+
require 'shoulda/active_record/matchers/validate_format_of_matcher'
|
7
8
|
require 'shoulda/active_record/matchers/validate_uniqueness_of_matcher'
|
8
9
|
require 'shoulda/active_record/matchers/validate_acceptance_of_matcher'
|
9
10
|
require 'shoulda/active_record/matchers/validate_numericality_of_matcher'
|
10
11
|
require 'shoulda/active_record/matchers/association_matcher'
|
11
12
|
require 'shoulda/active_record/matchers/have_db_column_matcher'
|
12
|
-
require 'shoulda/active_record/matchers/
|
13
|
+
require 'shoulda/active_record/matchers/have_db_index_matcher'
|
13
14
|
require 'shoulda/active_record/matchers/have_readonly_attribute_matcher'
|
14
15
|
require 'shoulda/active_record/matchers/allow_mass_assignment_of_matcher'
|
15
16
|
require 'shoulda/active_record/matchers/have_named_scope_matcher'
|
@@ -6,8 +6,8 @@ module Shoulda # :nodoc:
|
|
6
6
|
#
|
7
7
|
# Options:
|
8
8
|
# * <tt>with_message</tt> - value the test expects to find in
|
9
|
-
# <tt>errors.on(:attribute)</tt>. Regexp or string.
|
10
|
-
#
|
9
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. If omitted,
|
10
|
+
# the test looks for any errors in <tt>errors.on(:attribute)</tt>.
|
11
11
|
#
|
12
12
|
# Example:
|
13
13
|
# it { should_not allow_value('bad').for(:isbn) }
|
@@ -36,7 +36,6 @@ module Shoulda # :nodoc:
|
|
36
36
|
|
37
37
|
def matches?(instance)
|
38
38
|
@instance = instance
|
39
|
-
@expected_message ||= :invalid
|
40
39
|
if Symbol === @expected_message
|
41
40
|
@expected_message = default_error_message(@expected_message)
|
42
41
|
end
|
@@ -62,7 +61,7 @@ module Shoulda # :nodoc:
|
|
62
61
|
@instance.valid?
|
63
62
|
@errors = @instance.errors.on(@attribute)
|
64
63
|
@errors = [@errors] unless @errors.is_a?(Array)
|
65
|
-
errors_match_regexp? || errors_match_string?
|
64
|
+
@expected_message ? (errors_match_regexp? || errors_match_string?) : (@errors != [nil])
|
66
65
|
end
|
67
66
|
|
68
67
|
def errors_match_regexp?
|
@@ -84,7 +83,8 @@ module Shoulda # :nodoc:
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def expectation
|
87
|
-
"errors
|
86
|
+
"errors " <<
|
87
|
+
(@expected_message ? "to include #{@expected_message.inspect} " : "") <<
|
88
88
|
"when #{@attribute} is set to #{@value.inspect}"
|
89
89
|
end
|
90
90
|
|
@@ -133,7 +133,7 @@ module Shoulda # :nodoc:
|
|
133
133
|
|
134
134
|
def through_association_exists?
|
135
135
|
if through_reflection.nil?
|
136
|
-
"#{model_class.name} does not have any relationship to #{@through}"
|
136
|
+
@missing = "#{model_class.name} does not have any relationship to #{@through}"
|
137
137
|
false
|
138
138
|
else
|
139
139
|
true
|
@@ -142,10 +142,10 @@ module Shoulda # :nodoc:
|
|
142
142
|
|
143
143
|
def through_association_correct?
|
144
144
|
if @through == reflection.options[:through]
|
145
|
-
"Expected #{model_class.name} to have #{@name} through #{@through}, " <<
|
146
|
-
" but got it through #{reflection.options[:through]}"
|
147
145
|
true
|
148
146
|
else
|
147
|
+
@missing = "Expected #{model_class.name} to have #{@name} through #{@through}, " <<
|
148
|
+
"but got it through #{reflection.options[:through]}"
|
149
149
|
false
|
150
150
|
end
|
151
151
|
end
|
@@ -14,15 +14,15 @@ module Shoulda # :nodoc:
|
|
14
14
|
#
|
15
15
|
# Examples:
|
16
16
|
#
|
17
|
-
# it { should
|
18
|
-
# it { should
|
19
|
-
# it { should
|
17
|
+
# it { should have_db_index(:age) }
|
18
|
+
# it { should have_db_index([:commentable_type, :commentable_id]) }
|
19
|
+
# it { should have_db_index(:ssn).unique(true) }
|
20
20
|
#
|
21
|
-
def
|
22
|
-
|
21
|
+
def have_db_index(columns)
|
22
|
+
HaveDbIndexMatcher.new(:have_index, columns)
|
23
23
|
end
|
24
24
|
|
25
|
-
class
|
25
|
+
class HaveDbIndexMatcher # :nodoc:
|
26
26
|
def initialize(macro, columns)
|
27
27
|
@macro = macro
|
28
28
|
@columns = normalize_columns_to_array(columns)
|
@@ -47,7 +47,7 @@ module Shoulda # :nodoc:
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def description
|
50
|
-
"have a #{index_type} index on columns #{@columns}"
|
50
|
+
"have a #{index_type} index on columns #{@columns.join(' and ')}"
|
51
51
|
end
|
52
52
|
|
53
53
|
protected
|
@@ -88,7 +88,14 @@ module Shoulda # :nodoc:
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def index_type
|
91
|
-
@unique
|
91
|
+
case @unique
|
92
|
+
when nil
|
93
|
+
''
|
94
|
+
when false
|
95
|
+
'non-unique'
|
96
|
+
else
|
97
|
+
'unique'
|
98
|
+
end
|
92
99
|
end
|
93
100
|
|
94
101
|
def normalize_columns_to_array(columns)
|
@@ -2,6 +2,8 @@ module Shoulda # :nodoc:
|
|
2
2
|
module ActiveRecord # :nodoc:
|
3
3
|
module Matchers
|
4
4
|
|
5
|
+
# Deprecated.
|
6
|
+
#
|
5
7
|
# Ensures that the model has a method named scope_call that returns a
|
6
8
|
# NamedScope object with the proxy options set to the options you supply.
|
7
9
|
# scope_call can be either a symbol, or a Ruby expression in a String
|
@@ -43,6 +45,7 @@ module Shoulda # :nodoc:
|
|
43
45
|
# end
|
44
46
|
#
|
45
47
|
def have_named_scope(scope_call)
|
48
|
+
warn "[DEPRECATION] should_have_named_scope is deprecated."
|
46
49
|
HaveNamedScopeMatcher.new(scope_call).in_context(self)
|
47
50
|
end
|
48
51
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveRecord # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the model is not valid if the given attribute is not
|
6
|
+
# formatted correctly.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
11
|
+
# Defaults to the translation for <tt>:blank</tt>.
|
12
|
+
# * <tt>with(string to test against)</tt>
|
13
|
+
# * <tt>not_with(string to test against)</tt>
|
14
|
+
#
|
15
|
+
# Examples:
|
16
|
+
# it { should validate_format_of(:name).
|
17
|
+
# with('12345').
|
18
|
+
# with_message(/is not optional/) }
|
19
|
+
# it { should validate_format_of(:name).
|
20
|
+
# not_with('12D45').
|
21
|
+
# with_message(/is not optional/) }
|
22
|
+
#
|
23
|
+
def validate_format_of(attr)
|
24
|
+
ValidateFormatOfMatcher.new(attr)
|
25
|
+
end
|
26
|
+
|
27
|
+
class ValidateFormatOfMatcher < ValidationMatcher # :nodoc:
|
28
|
+
|
29
|
+
def initialize(attribute)
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_message(message)
|
34
|
+
@expected_message = message if message
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def with(value)
|
39
|
+
raise "You may not call both with and not_with" if @value_to_fail
|
40
|
+
@value_to_pass = value
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def not_with(value)
|
46
|
+
raise "You may not call both with and not_with" if @value_to_pass
|
47
|
+
@value_to_fail = value
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def matches?(subject)
|
53
|
+
super(subject)
|
54
|
+
@expected_message ||= :blank
|
55
|
+
return disallows_value_of(@value_to_fail, @expected_message) if @value_to_fail
|
56
|
+
allows_value_of(@value_to_pass, @expected_message) if @value_to_pass
|
57
|
+
end
|
58
|
+
|
59
|
+
def description
|
60
|
+
"#{@attribute} have a valid format"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/shoulda/assertions.rb
CHANGED
@@ -45,15 +45,27 @@ module Shoulda # :nodoc:
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Asserts that the given matcher returns true when +target+ is passed to #matches?
|
48
|
-
def assert_accepts(matcher, target)
|
49
|
-
|
50
|
-
|
48
|
+
def assert_accepts(matcher, target, options = {})
|
49
|
+
if matcher.matches?(target)
|
50
|
+
assert_block { true }
|
51
|
+
if options[:message]
|
52
|
+
assert_match options[:message], matcher.negative_failure_message
|
53
|
+
end
|
54
|
+
else
|
55
|
+
assert_block(matcher.failure_message) { false }
|
56
|
+
end
|
51
57
|
end
|
52
58
|
|
53
59
|
# Asserts that the given matcher returns false when +target+ is passed to #matches?
|
54
|
-
def assert_rejects(matcher, target)
|
55
|
-
|
56
|
-
|
60
|
+
def assert_rejects(matcher, target, options = {})
|
61
|
+
unless matcher.matches?(target)
|
62
|
+
assert_block { true }
|
63
|
+
if options[:message]
|
64
|
+
assert_match options[:message], matcher.failure_message
|
65
|
+
end
|
66
|
+
else
|
67
|
+
assert_block(matcher.negative_failure_message) { false }
|
68
|
+
end
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
data/lib/shoulda/context.rb
CHANGED
@@ -59,7 +59,7 @@ module Shoulda
|
|
59
59
|
|
60
60
|
def should(name, options = {}, &blk)
|
61
61
|
if Shoulda.current_context
|
62
|
-
block_given? ? Shoulda.current_context.should(name, options, &blk) :
|
62
|
+
block_given? ? Shoulda.current_context.should(name, options, &blk) : Shoulda.current_context.should_eventually(name)
|
63
63
|
else
|
64
64
|
context_name = self.name.gsub(/Test/, "")
|
65
65
|
context = Shoulda::Context.new(context_name, self) do
|
@@ -170,6 +170,98 @@ module Shoulda
|
|
170
170
|
context.build
|
171
171
|
end
|
172
172
|
end
|
173
|
+
|
174
|
+
# Returns the class being tested, as determined by the test class name.
|
175
|
+
#
|
176
|
+
# class UserTest; described_type; end
|
177
|
+
# # => User
|
178
|
+
def described_type
|
179
|
+
self.name.gsub(/Test$/, '').constantize
|
180
|
+
end
|
181
|
+
|
182
|
+
# Sets the return value of the subject instance method:
|
183
|
+
#
|
184
|
+
# class UserTest < Test::Unit::TestCase
|
185
|
+
# subject { User.first }
|
186
|
+
#
|
187
|
+
# # uses the existing user
|
188
|
+
# should_validate_uniqueness_of :email
|
189
|
+
# end
|
190
|
+
def subject(&block)
|
191
|
+
@subject_block = block
|
192
|
+
end
|
193
|
+
|
194
|
+
def subject_block # :nodoc:
|
195
|
+
@subject_block
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
module InstanceMethods
|
200
|
+
# Returns an instance of the class under test.
|
201
|
+
#
|
202
|
+
# class UserTest
|
203
|
+
# should "be a user" do
|
204
|
+
# assert_kind_of User, subject # passes
|
205
|
+
# end
|
206
|
+
# end
|
207
|
+
#
|
208
|
+
# The subject can be explicitly set using the subject class method:
|
209
|
+
#
|
210
|
+
# class UserTest
|
211
|
+
# subject { User.first }
|
212
|
+
# should "be an existing user" do
|
213
|
+
# assert !subject.new_record? # uses the first user
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
#
|
217
|
+
# If an instance variable exists named after the described class, that
|
218
|
+
# instance variable will be used as the subject. This behavior is
|
219
|
+
# deprecated, and will be removed in a future version of Shoulda. The
|
220
|
+
# recommended approach for using a different subject is to use the subject
|
221
|
+
# class method.
|
222
|
+
#
|
223
|
+
# class UserTest
|
224
|
+
# should "be the existing user" do
|
225
|
+
# @user = User.new
|
226
|
+
# assert_equal @user, subject # passes
|
227
|
+
# end
|
228
|
+
# end
|
229
|
+
#
|
230
|
+
# The subject is used by all macros that require an instance of the class
|
231
|
+
# being tested.
|
232
|
+
def subject
|
233
|
+
if subject_block
|
234
|
+
instance_eval(&subject_block)
|
235
|
+
else
|
236
|
+
get_instance_of(self.class.described_type)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def subject_block # :nodoc:
|
241
|
+
(@shoulda_context && @shoulda_context.subject_block) || self.class.subject_block
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_instance_of(object_or_klass) # :nodoc:
|
245
|
+
if object_or_klass.is_a?(Class)
|
246
|
+
klass = object_or_klass
|
247
|
+
ivar = "@#{instance_variable_name_for(klass)}"
|
248
|
+
if instance = instance_variable_get(ivar)
|
249
|
+
warn "[WARNING] Using #{ivar} as the subject. Future versions " <<
|
250
|
+
"of Shoulda will require an explicit subject using the " <<
|
251
|
+
"subject class method. Add this after your setup to avoid " <<
|
252
|
+
"this warning: subject { #{ivar} }"
|
253
|
+
instance
|
254
|
+
else
|
255
|
+
klass.new
|
256
|
+
end
|
257
|
+
else
|
258
|
+
object_or_klass
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def instance_variable_name_for(klass) # :nodoc:
|
263
|
+
klass.to_s.split('::').last.underscore
|
264
|
+
end
|
173
265
|
end
|
174
266
|
|
175
267
|
class Context # :nodoc:
|
@@ -181,6 +273,7 @@ module Shoulda
|
|
181
273
|
attr_accessor :teardown_blocks # blocks given via teardown methods
|
182
274
|
attr_accessor :shoulds # array of hashes representing the should statements
|
183
275
|
attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
|
276
|
+
attr_accessor :subject_block
|
184
277
|
|
185
278
|
def initialize(name, parent, &blk)
|
186
279
|
Shoulda.add_context(self)
|
@@ -224,6 +317,10 @@ module Shoulda
|
|
224
317
|
self.should_eventuallys << { :name => name, :block => blk }
|
225
318
|
end
|
226
319
|
|
320
|
+
def subject(&block)
|
321
|
+
self.subject_block = block
|
322
|
+
end
|
323
|
+
|
227
324
|
def full_name
|
228
325
|
parent_name = parent.full_name if am_subcontext?
|
229
326
|
return [parent_name, name].join(" ").strip
|
@@ -246,6 +343,7 @@ module Shoulda
|
|
246
343
|
|
247
344
|
context = self
|
248
345
|
test_unit_class.send(:define_method, test_name) do
|
346
|
+
@shoulda_context = context
|
249
347
|
begin
|
250
348
|
context.run_parent_setup_blocks(self)
|
251
349
|
should[:before].bind(self).call if should[:before]
|
data/lib/shoulda/macros.rb
CHANGED
@@ -3,70 +3,130 @@ require 'shoulda/private_helpers'
|
|
3
3
|
module Shoulda # :nodoc:
|
4
4
|
module Macros
|
5
5
|
# Macro that creates a test asserting a change between the return value
|
6
|
-
# of
|
6
|
+
# of a block that is run before and after the current setup block
|
7
7
|
# is run. This is similar to Active Support's <tt>assert_difference</tt>
|
8
8
|
# assertion, but supports more than just numeric values. See also
|
9
9
|
# should_not_change.
|
10
10
|
#
|
11
|
+
# The passed description will be used when generating the test name and failure messages.
|
12
|
+
#
|
11
13
|
# Example:
|
12
14
|
#
|
13
15
|
# context "Creating a post" do
|
14
16
|
# setup { Post.create }
|
15
|
-
# should_change "
|
17
|
+
# should_change("the number of posts", :by => 1) { Post.count }
|
16
18
|
# end
|
17
19
|
#
|
18
20
|
# As shown in this example, the <tt>:by</tt> option expects a numeric
|
19
21
|
# difference between the before and after values of the expression. You
|
20
22
|
# may also specify <tt>:from</tt> and <tt>:to</tt> options:
|
21
23
|
#
|
22
|
-
# should_change "
|
23
|
-
# should_change
|
24
|
+
# should_change("the number of posts", :from => 0, :to => 1) { Post.count }
|
25
|
+
# should_change("the post title", :from => "old", :to => "new") { @post.title }
|
24
26
|
#
|
25
27
|
# Combinations of <tt>:by</tt>, <tt>:from</tt>, and <tt>:to</tt> are allowed:
|
26
28
|
#
|
27
|
-
#
|
28
|
-
# should_change "@post.title
|
29
|
-
#
|
30
|
-
|
29
|
+
# # Assert the value changed in some way:
|
30
|
+
# should_change("the post title") { @post.title }
|
31
|
+
#
|
32
|
+
# # Assert the value changed to anything other than "old:"
|
33
|
+
# should_change("the post title", :from => "old") { @post.title }
|
34
|
+
#
|
35
|
+
# # Assert the value changed to "new:"
|
36
|
+
# should_change("the post title", :to => "new") { @post.title }
|
37
|
+
def should_change(description, options = {}, &block)
|
31
38
|
by, from, to = get_options!([options], :by, :from, :to)
|
32
|
-
stmt = "change #{
|
39
|
+
stmt = "change #{description}"
|
33
40
|
stmt << " from #{from.inspect}" if from
|
34
41
|
stmt << " to #{to.inspect}" if to
|
35
42
|
stmt << " by #{by.inspect}" if by
|
36
43
|
|
37
|
-
|
38
|
-
|
44
|
+
if block_given?
|
45
|
+
code = block
|
46
|
+
else
|
47
|
+
warn "[DEPRECATION] should_change(expression, options) is deprecated. " <<
|
48
|
+
"Use should_change(description, options) { code } instead."
|
49
|
+
code = lambda { eval(description) }
|
50
|
+
end
|
51
|
+
before = lambda { @_before_should_change = code.bind(self).call }
|
39
52
|
should stmt, :before => before do
|
40
53
|
old_value = @_before_should_change
|
41
|
-
new_value =
|
42
|
-
assert_operator from, :===, old_value, "#{
|
43
|
-
assert_not_equal old_value, new_value, "#{
|
44
|
-
assert_operator to, :===, new_value, "#{
|
54
|
+
new_value = code.bind(self).call
|
55
|
+
assert_operator from, :===, old_value, "#{description} did not originally match #{from.inspect}" if from
|
56
|
+
assert_not_equal old_value, new_value, "#{description} did not change" unless by == 0
|
57
|
+
assert_operator to, :===, new_value, "#{description} was not changed to match #{to.inspect}" if to
|
45
58
|
assert_equal old_value + by, new_value if by
|
46
59
|
end
|
47
60
|
end
|
48
61
|
|
49
62
|
# Macro that creates a test asserting no change between the return value
|
50
|
-
# of
|
63
|
+
# of a block that is run before and after the current setup block
|
51
64
|
# is run. This is the logical opposite of should_change.
|
52
65
|
#
|
66
|
+
# The passed description will be used when generating the test name and failure message.
|
67
|
+
#
|
53
68
|
# Example:
|
54
69
|
#
|
55
70
|
# context "Updating a post" do
|
56
71
|
# setup { @post.update_attributes(:title => "new") }
|
57
|
-
# should_not_change "Post.count
|
72
|
+
# should_not_change("the number of posts") { Post.count }
|
58
73
|
# end
|
59
|
-
def should_not_change(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
74
|
+
def should_not_change(description, &block)
|
75
|
+
if block_given?
|
76
|
+
code = block
|
77
|
+
else
|
78
|
+
warn "[DEPRECATION] should_not_change(expression) is deprecated. " <<
|
79
|
+
"Use should_not_change(description) { code } instead."
|
80
|
+
code = lambda { eval(description) }
|
81
|
+
end
|
82
|
+
before = lambda { @_before_should_not_change = code.bind(self).call }
|
83
|
+
should "not change #{description}", :before => before do
|
84
|
+
new_value = code.bind(self).call
|
85
|
+
assert_equal @_before_should_not_change, new_value, "#{description} changed"
|
65
86
|
end
|
66
87
|
end
|
67
88
|
|
89
|
+
# Macro that creates a test asserting that a record of the given class was
|
90
|
+
# created.
|
91
|
+
#
|
92
|
+
# Example:
|
93
|
+
#
|
94
|
+
# context "creating a post" do
|
95
|
+
# setup { Post.create(post_attributes) }
|
96
|
+
# should_create :post
|
97
|
+
# end
|
98
|
+
def should_create(class_name)
|
99
|
+
should_change_record_count_of(class_name, 1, 'create')
|
100
|
+
end
|
101
|
+
|
102
|
+
# Macro that creates a test asserting that a record of the given class was
|
103
|
+
# destroyed.
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
#
|
107
|
+
# context "destroying a post" do
|
108
|
+
# setup { Post.first.destroy }
|
109
|
+
# should_destroy :post
|
110
|
+
# end
|
111
|
+
def should_destroy(class_name)
|
112
|
+
should_change_record_count_of(class_name, -1, 'destroy')
|
113
|
+
end
|
114
|
+
|
68
115
|
private
|
69
116
|
|
117
|
+
def should_change_record_count_of(class_name, amount, action) # :nodoc:
|
118
|
+
klass = class_name.to_s.camelize.constantize
|
119
|
+
before = lambda do
|
120
|
+
@_before_change_record_count = klass.count
|
121
|
+
end
|
122
|
+
human_name = class_name.to_s.humanize.downcase
|
123
|
+
should "#{action} a #{human_name}", :before => before do
|
124
|
+
assert_equal @_before_change_record_count + amount,
|
125
|
+
klass.count,
|
126
|
+
"Expected to #{action} a #{human_name}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
70
130
|
include Shoulda::Private
|
71
131
|
end
|
72
132
|
end
|