robert-shoulda 2.10.3

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 (169) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
  2. data/MIT-LICENSE +22 -0
  3. data/README.rdoc +171 -0
  4. data/Rakefile +72 -0
  5. data/bin/convert_to_should_syntax +42 -0
  6. data/lib/shoulda/action_controller/macros.rb +240 -0
  7. data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +109 -0
  8. data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +57 -0
  9. data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +81 -0
  10. data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +74 -0
  11. data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +81 -0
  12. data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
  13. data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +87 -0
  14. data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +85 -0
  15. data/lib/shoulda/action_controller/matchers.rb +37 -0
  16. data/lib/shoulda/action_controller.rb +26 -0
  17. data/lib/shoulda/action_mailer/assertions.rb +38 -0
  18. data/lib/shoulda/action_mailer.rb +10 -0
  19. data/lib/shoulda/action_view/macros.rb +61 -0
  20. data/lib/shoulda/action_view.rb +10 -0
  21. data/lib/shoulda/active_record/assertions.rb +69 -0
  22. data/lib/shoulda/active_record/helpers.rb +27 -0
  23. data/lib/shoulda/active_record/macros.rb +571 -0
  24. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  25. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  26. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  27. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  28. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  29. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  30. data/lib/shoulda/active_record/matchers/have_db_index_matcher.rb +112 -0
  31. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +128 -0
  32. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  33. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  34. data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +67 -0
  35. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  36. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  37. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  38. data/lib/shoulda/active_record/matchers/validation_matcher.rb +57 -0
  39. data/lib/shoulda/active_record/matchers.rb +43 -0
  40. data/lib/shoulda/active_record.rb +16 -0
  41. data/lib/shoulda/assertions.rb +71 -0
  42. data/lib/shoulda/autoload_macros.rb +46 -0
  43. data/lib/shoulda/context.rb +413 -0
  44. data/lib/shoulda/helpers.rb +8 -0
  45. data/lib/shoulda/macros.rb +133 -0
  46. data/lib/shoulda/private_helpers.rb +13 -0
  47. data/lib/shoulda/proc_extensions.rb +14 -0
  48. data/lib/shoulda/rails.rb +13 -0
  49. data/lib/shoulda/rspec.rb +11 -0
  50. data/lib/shoulda/tasks/list_tests.rake +29 -0
  51. data/lib/shoulda/tasks/yaml_to_shoulda.rake +28 -0
  52. data/lib/shoulda/tasks.rb +3 -0
  53. data/lib/shoulda/test_unit.rb +22 -0
  54. data/lib/shoulda.rb +9 -0
  55. data/rails/init.rb +7 -0
  56. data/test/README +36 -0
  57. data/test/fail_macros.rb +39 -0
  58. data/test/fixtures/addresses.yml +3 -0
  59. data/test/fixtures/friendships.yml +0 -0
  60. data/test/fixtures/posts.yml +5 -0
  61. data/test/fixtures/products.yml +0 -0
  62. data/test/fixtures/taggings.yml +0 -0
  63. data/test/fixtures/tags.yml +9 -0
  64. data/test/fixtures/users.yml +6 -0
  65. data/test/functional/posts_controller_test.rb +121 -0
  66. data/test/functional/users_controller_test.rb +19 -0
  67. data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +68 -0
  68. data/test/matchers/active_record/allow_value_matcher_test.rb +64 -0
  69. data/test/matchers/active_record/association_matcher_test.rb +263 -0
  70. data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +80 -0
  71. data/test/matchers/active_record/ensure_length_of_matcher_test.rb +158 -0
  72. data/test/matchers/active_record/have_db_column_matcher_test.rb +169 -0
  73. data/test/matchers/active_record/have_db_index_matcher_test.rb +91 -0
  74. data/test/matchers/active_record/have_named_scope_matcher_test.rb +65 -0
  75. data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +29 -0
  76. data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +44 -0
  77. data/test/matchers/active_record/validate_format_of_matcher_test.rb +39 -0
  78. data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +52 -0
  79. data/test/matchers/active_record/validate_presence_of_matcher_test.rb +86 -0
  80. data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +147 -0
  81. data/test/matchers/controller/assign_to_matcher_test.rb +35 -0
  82. data/test/matchers/controller/filter_param_matcher_test.rb +32 -0
  83. data/test/matchers/controller/render_with_layout_matcher_test.rb +33 -0
  84. data/test/matchers/controller/respond_with_content_type_matcher_test.rb +32 -0
  85. data/test/matchers/controller/respond_with_matcher_test.rb +106 -0
  86. data/test/matchers/controller/route_matcher_test.rb +75 -0
  87. data/test/matchers/controller/set_session_matcher_test.rb +38 -0
  88. data/test/matchers/controller/set_the_flash_matcher.rb +41 -0
  89. data/test/model_builder.rb +106 -0
  90. data/test/other/autoload_macro_test.rb +18 -0
  91. data/test/other/context_test.rb +203 -0
  92. data/test/other/convert_to_should_syntax_test.rb +63 -0
  93. data/test/other/helpers_test.rb +340 -0
  94. data/test/other/private_helpers_test.rb +32 -0
  95. data/test/other/should_test.rb +271 -0
  96. data/test/rails_root/app/controllers/application_controller.rb +25 -0
  97. data/test/rails_root/app/controllers/posts_controller.rb +87 -0
  98. data/test/rails_root/app/controllers/users_controller.rb +84 -0
  99. data/test/rails_root/app/helpers/application_helper.rb +3 -0
  100. data/test/rails_root/app/helpers/posts_helper.rb +2 -0
  101. data/test/rails_root/app/helpers/users_helper.rb +2 -0
  102. data/test/rails_root/app/models/address.rb +7 -0
  103. data/test/rails_root/app/models/flea.rb +3 -0
  104. data/test/rails_root/app/models/friendship.rb +4 -0
  105. data/test/rails_root/app/models/pets/cat.rb +7 -0
  106. data/test/rails_root/app/models/pets/dog.rb +10 -0
  107. data/test/rails_root/app/models/post.rb +12 -0
  108. data/test/rails_root/app/models/product.rb +12 -0
  109. data/test/rails_root/app/models/profile.rb +2 -0
  110. data/test/rails_root/app/models/registration.rb +2 -0
  111. data/test/rails_root/app/models/tag.rb +8 -0
  112. data/test/rails_root/app/models/tagging.rb +4 -0
  113. data/test/rails_root/app/models/treat.rb +3 -0
  114. data/test/rails_root/app/models/user.rb +32 -0
  115. data/test/rails_root/app/views/layouts/posts.rhtml +19 -0
  116. data/test/rails_root/app/views/layouts/users.rhtml +17 -0
  117. data/test/rails_root/app/views/layouts/wide.html.erb +1 -0
  118. data/test/rails_root/app/views/posts/edit.rhtml +27 -0
  119. data/test/rails_root/app/views/posts/index.rhtml +25 -0
  120. data/test/rails_root/app/views/posts/new.rhtml +26 -0
  121. data/test/rails_root/app/views/posts/show.rhtml +18 -0
  122. data/test/rails_root/app/views/users/edit.rhtml +22 -0
  123. data/test/rails_root/app/views/users/index.rhtml +22 -0
  124. data/test/rails_root/app/views/users/new.rhtml +21 -0
  125. data/test/rails_root/app/views/users/show.rhtml +13 -0
  126. data/test/rails_root/config/boot.rb +110 -0
  127. data/test/rails_root/config/database.yml +4 -0
  128. data/test/rails_root/config/environment.rb +18 -0
  129. data/test/rails_root/config/environments/test.rb +0 -0
  130. data/test/rails_root/config/initializers/new_rails_defaults.rb +15 -0
  131. data/test/rails_root/config/initializers/shoulda.rb +8 -0
  132. data/test/rails_root/config/routes.rb +6 -0
  133. data/test/rails_root/db/migrate/001_create_users.rb +19 -0
  134. data/test/rails_root/db/migrate/002_create_posts.rb +13 -0
  135. data/test/rails_root/db/migrate/003_create_taggings.rb +12 -0
  136. data/test/rails_root/db/migrate/004_create_tags.rb +11 -0
  137. data/test/rails_root/db/migrate/005_create_dogs.rb +12 -0
  138. data/test/rails_root/db/migrate/006_create_addresses.rb +14 -0
  139. data/test/rails_root/db/migrate/007_create_fleas.rb +11 -0
  140. data/test/rails_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  141. data/test/rails_root/db/migrate/009_create_products.rb +17 -0
  142. data/test/rails_root/db/migrate/010_create_friendships.rb +14 -0
  143. data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
  144. data/test/rails_root/db/migrate/20090506203502_create_profiles.rb +12 -0
  145. data/test/rails_root/db/migrate/20090506203536_create_registrations.rb +14 -0
  146. data/test/rails_root/db/migrate/20090513104502_create_cats.rb +12 -0
  147. data/test/rails_root/db/schema.rb +0 -0
  148. data/test/rails_root/log/test.log +8963 -0
  149. data/test/rails_root/public/404.html +30 -0
  150. data/test/rails_root/public/422.html +30 -0
  151. data/test/rails_root/public/500.html +30 -0
  152. data/test/rails_root/script/console +3 -0
  153. data/test/rails_root/script/generate +3 -0
  154. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  155. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  156. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  157. data/test/rspec_test.rb +207 -0
  158. data/test/test_helper.rb +28 -0
  159. data/test/unit/address_test.rb +15 -0
  160. data/test/unit/cat_test.rb +7 -0
  161. data/test/unit/dog_test.rb +9 -0
  162. data/test/unit/flea_test.rb +6 -0
  163. data/test/unit/friendship_test.rb +6 -0
  164. data/test/unit/post_test.rb +19 -0
  165. data/test/unit/product_test.rb +23 -0
  166. data/test/unit/tag_test.rb +15 -0
  167. data/test/unit/tagging_test.rb +6 -0
  168. data/test/unit/user_test.rb +80 -0
  169. metadata +225 -0
@@ -0,0 +1,413 @@
1
+ module Shoulda
2
+ class << self
3
+ attr_accessor :contexts
4
+ def contexts # :nodoc:
5
+ @contexts ||= []
6
+ end
7
+
8
+ def current_context # :nodoc:
9
+ self.contexts.last
10
+ end
11
+
12
+ def add_context(context) # :nodoc:
13
+ self.contexts.push(context)
14
+ end
15
+
16
+ def remove_context # :nodoc:
17
+ self.contexts.pop
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # == Should statements
23
+ #
24
+ # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
25
+ # contains all the normal code and assertions you're used to seeing, with the added benefit that
26
+ # they can be wrapped inside context blocks (see below).
27
+ #
28
+ # === Example:
29
+ #
30
+ # class UserTest < Test::Unit::TestCase
31
+ #
32
+ # def setup
33
+ # @user = User.new("John", "Doe")
34
+ # end
35
+ #
36
+ # should "return its full name"
37
+ # assert_equal 'John Doe', @user.full_name
38
+ # end
39
+ #
40
+ # end
41
+ #
42
+ # ...will produce the following test:
43
+ # * <tt>"test: User should return its full name. "</tt>
44
+ #
45
+ # Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
46
+ #
47
+ # Should statements can also take a Proc as a <tt>:before </tt>option. This proc runs after any
48
+ # parent context's setups but before the current context's setup.
49
+ #
50
+ # === Example:
51
+ #
52
+ # context "Some context" do
53
+ # setup { puts("I run after the :before proc") }
54
+ #
55
+ # should "run a :before proc", :before => lambda { puts("I run before the setup") } do
56
+ # assert true
57
+ # end
58
+ # end
59
+
60
+ def should(name, options = {}, &blk)
61
+ if Shoulda.current_context
62
+ block_given? ? Shoulda.current_context.should(name, options, &blk) : Shoulda.current_context.should_eventually(name)
63
+ else
64
+ context_name = self.name.gsub(/Test/, "")
65
+ context = Shoulda::Context.new(context_name, self) do
66
+ block_given? ? should(name, options, &blk) : should_eventually(name)
67
+ end
68
+ context.build
69
+ end
70
+ end
71
+
72
+ # == Before statements
73
+ #
74
+ # Before statements are should statements that run before the current
75
+ # context's setup. These are especially useful when setting expectations.
76
+ #
77
+ # === Example:
78
+ #
79
+ # class UserControllerTest < Test::Unit::TestCase
80
+ # context "the index action" do
81
+ # setup do
82
+ # @users = [Factory(:user)]
83
+ # User.stubs(:find).returns(@users)
84
+ # end
85
+ #
86
+ # context "on GET" do
87
+ # setup { get :index }
88
+ #
89
+ # should_respond_with :success
90
+ #
91
+ # # runs before "get :index"
92
+ # before_should "find all users" do
93
+ # User.expects(:find).with(:all).returns(@users)
94
+ # end
95
+ # end
96
+ # end
97
+ # end
98
+ def before_should(name, &blk)
99
+ should(name, :before => blk) { assert true }
100
+ end
101
+
102
+ # Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
103
+ def should_eventually(name, options = {}, &blk)
104
+ context_name = self.name.gsub(/Test/, "")
105
+ context = Shoulda::Context.new(context_name, self) do
106
+ should_eventually(name, &blk)
107
+ end
108
+ context.build
109
+ end
110
+
111
+ # == Contexts
112
+ #
113
+ # A context block groups should statements under a common set of setup/teardown methods.
114
+ # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
115
+ # and readability of your test code.
116
+ #
117
+ # A context block can contain setup, should, should_eventually, and teardown blocks.
118
+ #
119
+ # class UserTest < Test::Unit::TestCase
120
+ # context "A User instance" do
121
+ # setup do
122
+ # @user = User.find(:first)
123
+ # end
124
+ #
125
+ # should "return its full name"
126
+ # assert_equal 'John Doe', @user.full_name
127
+ # end
128
+ # end
129
+ # end
130
+ #
131
+ # This code will produce the method <tt>"test: A User instance should return its full name. "</tt>.
132
+ #
133
+ # Contexts may be nested. Nested contexts run their setup blocks from out to in before each
134
+ # should statement. They then run their teardown blocks from in to out after each should statement.
135
+ #
136
+ # class UserTest < Test::Unit::TestCase
137
+ # context "A User instance" do
138
+ # setup do
139
+ # @user = User.find(:first)
140
+ # end
141
+ #
142
+ # should "return its full name"
143
+ # assert_equal 'John Doe', @user.full_name
144
+ # end
145
+ #
146
+ # context "with a profile" do
147
+ # setup do
148
+ # @user.profile = Profile.find(:first)
149
+ # end
150
+ #
151
+ # should "return true when sent :has_profile?"
152
+ # assert @user.has_profile?
153
+ # end
154
+ # end
155
+ # end
156
+ # end
157
+ #
158
+ # This code will produce the following methods
159
+ # * <tt>"test: A User instance should return its full name. "</tt>
160
+ # * <tt>"test: A User instance with a profile should return true when sent :has_profile?. "</tt>
161
+ #
162
+ # <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
163
+ # tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
164
+
165
+ def context(name, &blk)
166
+ if Shoulda.current_context
167
+ Shoulda.current_context.context(name, &blk)
168
+ else
169
+ context = Shoulda::Context.new(name, self, &blk)
170
+ context.build
171
+ end
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
+ @shoulda_subject ||= construct_subject
234
+ end
235
+
236
+ def subject_block # :nodoc:
237
+ (@shoulda_context && @shoulda_context.subject_block) || self.class.subject_block
238
+ end
239
+
240
+ def get_instance_of(object_or_klass) # :nodoc:
241
+ if object_or_klass.is_a?(Class)
242
+ klass = object_or_klass
243
+ ivar = "@#{instance_variable_name_for(klass)}"
244
+ if instance = instance_variable_get(ivar)
245
+ warn "[WARNING] Using #{ivar} as the subject. Future versions " <<
246
+ "of Shoulda will require an explicit subject using the " <<
247
+ "subject class method. Add this after your setup to avoid " <<
248
+ "this warning: subject { #{ivar} }"
249
+ instance
250
+ else
251
+ klass.new
252
+ end
253
+ else
254
+ object_or_klass
255
+ end
256
+ end
257
+
258
+ def instance_variable_name_for(klass) # :nodoc:
259
+ klass.to_s.split('::').last.underscore
260
+ end
261
+
262
+ private
263
+
264
+ def construct_subject
265
+ if subject_block
266
+ instance_eval(&subject_block)
267
+ else
268
+ get_instance_of(self.class.described_type)
269
+ end
270
+ end
271
+ end
272
+
273
+ class Context # :nodoc:
274
+
275
+ attr_accessor :name # my name
276
+ attr_accessor :parent # may be another context, or the original test::unit class.
277
+ attr_accessor :subcontexts # array of contexts nested under myself
278
+ attr_accessor :setup_blocks # blocks given via setup methods
279
+ attr_accessor :teardown_blocks # blocks given via teardown methods
280
+ attr_accessor :shoulds # array of hashes representing the should statements
281
+ attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
282
+ attr_accessor :subject_block
283
+
284
+ def initialize(name, parent, &blk)
285
+ Shoulda.add_context(self)
286
+ self.name = name
287
+ self.parent = parent
288
+ self.setup_blocks = []
289
+ self.teardown_blocks = []
290
+ self.shoulds = []
291
+ self.should_eventuallys = []
292
+ self.subcontexts = []
293
+
294
+ merge_block(&blk)
295
+ Shoulda.remove_context
296
+ end
297
+
298
+ def merge_block(&blk)
299
+ blk.bind(self).call
300
+ end
301
+
302
+ def context(name, &blk)
303
+ self.subcontexts << Context.new(name, self, &blk)
304
+ end
305
+
306
+ def setup(&blk)
307
+ self.setup_blocks << blk
308
+ end
309
+
310
+ def teardown(&blk)
311
+ self.teardown_blocks << blk
312
+ end
313
+
314
+ def should(name, options = {}, &blk)
315
+ if block_given?
316
+ self.shoulds << { :name => name, :before => options[:before], :block => blk }
317
+ else
318
+ self.should_eventuallys << { :name => name }
319
+ end
320
+ end
321
+
322
+ def should_eventually(name, &blk)
323
+ self.should_eventuallys << { :name => name, :block => blk }
324
+ end
325
+
326
+ def subject(&block)
327
+ self.subject_block = block
328
+ end
329
+
330
+ def subject_block
331
+ return @subject_block if @subject_block
332
+ parent.subject_block
333
+ end
334
+
335
+ def full_name
336
+ parent_name = parent.full_name if am_subcontext?
337
+ return [parent_name, name].join(" ").strip
338
+ end
339
+
340
+ def am_subcontext?
341
+ parent.is_a?(self.class) # my parent is the same class as myself.
342
+ end
343
+
344
+ def test_unit_class
345
+ am_subcontext? ? parent.test_unit_class : parent
346
+ end
347
+
348
+ def create_test_from_should_hash(should)
349
+ test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
350
+
351
+ if test_unit_class.instance_methods.include?(test_name.to_s)
352
+ warn " * WARNING: '#{test_name}' is already defined"
353
+ end
354
+
355
+ context = self
356
+ test_unit_class.send(:define_method, test_name) do
357
+ @shoulda_context = context
358
+ begin
359
+ context.run_parent_setup_blocks(self)
360
+ should[:before].bind(self).call if should[:before]
361
+ context.run_current_setup_blocks(self)
362
+ should[:block].bind(self).call
363
+ ensure
364
+ context.run_all_teardown_blocks(self)
365
+ end
366
+ end
367
+ end
368
+
369
+ def run_all_setup_blocks(binding)
370
+ run_parent_setup_blocks(binding)
371
+ run_current_setup_blocks(binding)
372
+ end
373
+
374
+ def run_parent_setup_blocks(binding)
375
+ self.parent.run_all_setup_blocks(binding) if am_subcontext?
376
+ end
377
+
378
+ def run_current_setup_blocks(binding)
379
+ setup_blocks.each do |setup_block|
380
+ setup_block.bind(binding).call
381
+ end
382
+ end
383
+
384
+ def run_all_teardown_blocks(binding)
385
+ teardown_blocks.reverse.each do |teardown_block|
386
+ teardown_block.bind(binding).call
387
+ end
388
+ self.parent.run_all_teardown_blocks(binding) if am_subcontext?
389
+ end
390
+
391
+ def print_should_eventuallys
392
+ should_eventuallys.each do |should|
393
+ test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
394
+ puts " * DEFERRED: " + test_name
395
+ end
396
+ end
397
+
398
+ def build
399
+ shoulds.each do |should|
400
+ create_test_from_should_hash(should)
401
+ end
402
+
403
+ subcontexts.each { |context| context.build }
404
+
405
+ print_should_eventuallys
406
+ end
407
+
408
+ def method_missing(method, *args, &blk)
409
+ test_unit_class.send(method, *args, &blk)
410
+ end
411
+
412
+ end
413
+ end
@@ -0,0 +1,8 @@
1
+ module Shoulda # :nodoc:
2
+ module Helpers
3
+ # Prints a message to stdout, tagged with the name of the calling method.
4
+ def report!(msg = "")
5
+ puts("#{caller.first}: #{msg}")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,133 @@
1
+ require 'shoulda/private_helpers'
2
+
3
+ module Shoulda # :nodoc:
4
+ module Macros
5
+ # Macro that creates a test asserting a change between the return value
6
+ # of a block that is run before and after the current setup block
7
+ # is run. This is similar to Active Support's <tt>assert_difference</tt>
8
+ # assertion, but supports more than just numeric values. See also
9
+ # should_not_change.
10
+ #
11
+ # The passed description will be used when generating the test name and failure messages.
12
+ #
13
+ # Example:
14
+ #
15
+ # context "Creating a post" do
16
+ # setup { Post.create }
17
+ # should_change("the number of posts", :by => 1) { Post.count }
18
+ # end
19
+ #
20
+ # As shown in this example, the <tt>:by</tt> option expects a numeric
21
+ # difference between the before and after values of the expression. You
22
+ # may also specify <tt>:from</tt> and <tt>:to</tt> options:
23
+ #
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 }
26
+ #
27
+ # Combinations of <tt>:by</tt>, <tt>:from</tt>, and <tt>:to</tt> are allowed:
28
+ #
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)
38
+ by, from, to = get_options!([options], :by, :from, :to)
39
+ stmt = "change #{description}"
40
+ stmt << " from #{from.inspect}" if from
41
+ stmt << " to #{to.inspect}" if to
42
+ stmt << " by #{by.inspect}" if by
43
+
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 }
52
+ should stmt, :before => before do
53
+ old_value = @_before_should_change
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
58
+ assert_equal old_value + by, new_value if by
59
+ end
60
+ end
61
+
62
+ # Macro that creates a test asserting no change between the return value
63
+ # of a block that is run before and after the current setup block
64
+ # is run. This is the logical opposite of should_change.
65
+ #
66
+ # The passed description will be used when generating the test name and failure message.
67
+ #
68
+ # Example:
69
+ #
70
+ # context "Updating a post" do
71
+ # setup { @post.update_attributes(:title => "new") }
72
+ # should_not_change("the number of posts") { Post.count }
73
+ # end
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"
86
+ end
87
+ end
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
+
115
+ private
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
+
130
+ include Shoulda::Private
131
+ end
132
+ end
133
+
@@ -0,0 +1,13 @@
1
+ module Shoulda # :nodoc:
2
+ module Private # :nodoc:
3
+ # Returns the values for the entries in the args hash who's keys are listed in the wanted array.
4
+ # Will raise if there are keys in the args hash that aren't listed.
5
+ def get_options!(args, *wanted)
6
+ ret = []
7
+ opts = (args.last.is_a?(Hash) ? args.pop : {})
8
+ wanted.each {|w| ret << opts.delete(w)}
9
+ raise ArgumentError, "Unsupported options given: #{opts.keys.join(', ')}" unless opts.keys.empty?
10
+ return wanted.size == 1 ? ret.first : ret
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # Stolen straight from ActiveSupport
2
+
3
+ class Proc #:nodoc:
4
+ def bind(object)
5
+ block, time = self, Time.now
6
+ (class << object; self end).class_eval do
7
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
8
+ define_method(method_name, &block)
9
+ method = instance_method(method_name)
10
+ remove_method(method_name)
11
+ method
12
+ end.bind(object)
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'shoulda'
4
+
5
+ require 'shoulda/active_record' if defined? ActiveRecord::Base
6
+ require 'shoulda/action_controller' if defined? ActionController::Base
7
+ require 'shoulda/action_view' if defined? ActionView::Base
8
+ require 'shoulda/action_mailer' if defined? ActionMailer::Base
9
+
10
+ if defined?(RAILS_ROOT)
11
+ # load in the 3rd party macros from vendorized plugins and gems
12
+ Shoulda.autoload_macros RAILS_ROOT, File.join("vendor", "{plugins,gems}", "*")
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'shoulda/active_record/matchers'
2
+ require 'shoulda/action_controller/matchers'
3
+ require 'active_support/test_case'
4
+
5
+ # :enddoc:
6
+ module ActiveSupport
7
+ class TestCase
8
+ include Shoulda::ActiveRecord::Matchers
9
+ include Shoulda::ActionController::Matchers
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ namespace :shoulda do
2
+ desc "List the names of the test methods in a specification like format"
3
+ task :list do
4
+ $LOAD_PATH.unshift("test")
5
+
6
+ require 'test/unit'
7
+ require 'rubygems'
8
+ require 'active_support'
9
+
10
+ # bug in test unit. Set to true to stop from running.
11
+ Test::Unit.run = true
12
+
13
+ test_files = Dir.glob(File.join('test', '**', '*_test.rb'))
14
+ test_files.each do |file|
15
+ load file
16
+ klass = File.basename(file, '.rb').classify
17
+ unless Object.const_defined?(klass.to_s)
18
+ puts "Skipping #{klass} because it doesn't map to a Class"
19
+ next
20
+ end
21
+ klass = klass.constantize
22
+
23
+ puts klass.name.gsub('Test', '')
24
+
25
+ test_methods = klass.instance_methods.grep(/^test/).map {|s| s.gsub(/^test: /, '')}.sort
26
+ test_methods.each {|m| puts " " + m }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ namespace :shoulda do
2
+ # From http://blog.internautdesign.com/2007/11/2/a-yaml_to_shoulda-rake-task
3
+ # David.Lowenfels@gmail.com
4
+ desc "Converts a YAML file (FILE=./path/to/yaml) into a Shoulda skeleton"
5
+ task :from_yaml do
6
+ require 'yaml'
7
+
8
+ def yaml_to_context(hash, indent = 0)
9
+ indent1 = ' ' * indent
10
+ indent2 = ' ' * (indent + 1)
11
+ hash.each_pair do |context, shoulds|
12
+ puts indent1 + "context \"#{context}\" do"
13
+ puts
14
+ shoulds.each do |should|
15
+ yaml_to_context( should, indent + 1 ) and next if should.is_a?( Hash )
16
+ puts indent2 + "should_eventually \"" + should.gsub(/^should +/,'') + "\" do"
17
+ puts indent2 + "end"
18
+ puts
19
+ end
20
+ puts indent1 + "end"
21
+ end
22
+ end
23
+
24
+ puts("Please pass in a FILE argument.") and exit unless ENV['FILE']
25
+
26
+ yaml_to_context( YAML.load_file( ENV['FILE'] ) )
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '*.rake')].each do |f|
2
+ load f
3
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+
3
+ require 'shoulda/context'
4
+ require 'shoulda/proc_extensions'
5
+ require 'shoulda/assertions'
6
+ require 'shoulda/macros'
7
+ require 'shoulda/helpers'
8
+ require 'shoulda/autoload_macros'
9
+ require 'shoulda/rails' if defined? RAILS_ROOT
10
+
11
+ module Test # :nodoc: all
12
+ module Unit
13
+ class TestCase
14
+ include Shoulda::InstanceMethods
15
+ extend Shoulda::ClassMethods
16
+ include Shoulda::Assertions
17
+ extend Shoulda::Macros
18
+ include Shoulda::Helpers
19
+ end
20
+ end
21
+ end
22
+