shoulda 2.10.1 → 2.10.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|