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.
- data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +171 -0
- data/Rakefile +72 -0
- data/bin/convert_to_should_syntax +42 -0
- data/lib/shoulda/action_controller/macros.rb +240 -0
- data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +109 -0
- data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +57 -0
- data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +81 -0
- data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +74 -0
- data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +81 -0
- data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
- data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +87 -0
- data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +85 -0
- data/lib/shoulda/action_controller/matchers.rb +37 -0
- data/lib/shoulda/action_controller.rb +26 -0
- data/lib/shoulda/action_mailer/assertions.rb +38 -0
- data/lib/shoulda/action_mailer.rb +10 -0
- data/lib/shoulda/action_view/macros.rb +61 -0
- data/lib/shoulda/action_view.rb +10 -0
- data/lib/shoulda/active_record/assertions.rb +69 -0
- data/lib/shoulda/active_record/helpers.rb +27 -0
- data/lib/shoulda/active_record/macros.rb +571 -0
- data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
- data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
- data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
- data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
- data/lib/shoulda/active_record/matchers/have_db_index_matcher.rb +112 -0
- data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +128 -0
- data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
- data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +67 -0
- data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
- data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/active_record/matchers/validation_matcher.rb +57 -0
- data/lib/shoulda/active_record/matchers.rb +43 -0
- data/lib/shoulda/active_record.rb +16 -0
- data/lib/shoulda/assertions.rb +71 -0
- data/lib/shoulda/autoload_macros.rb +46 -0
- data/lib/shoulda/context.rb +413 -0
- data/lib/shoulda/helpers.rb +8 -0
- data/lib/shoulda/macros.rb +133 -0
- data/lib/shoulda/private_helpers.rb +13 -0
- data/lib/shoulda/proc_extensions.rb +14 -0
- data/lib/shoulda/rails.rb +13 -0
- data/lib/shoulda/rspec.rb +11 -0
- data/lib/shoulda/tasks/list_tests.rake +29 -0
- data/lib/shoulda/tasks/yaml_to_shoulda.rake +28 -0
- data/lib/shoulda/tasks.rb +3 -0
- data/lib/shoulda/test_unit.rb +22 -0
- data/lib/shoulda.rb +9 -0
- data/rails/init.rb +7 -0
- data/test/README +36 -0
- data/test/fail_macros.rb +39 -0
- data/test/fixtures/addresses.yml +3 -0
- data/test/fixtures/friendships.yml +0 -0
- data/test/fixtures/posts.yml +5 -0
- data/test/fixtures/products.yml +0 -0
- data/test/fixtures/taggings.yml +0 -0
- data/test/fixtures/tags.yml +9 -0
- data/test/fixtures/users.yml +6 -0
- data/test/functional/posts_controller_test.rb +121 -0
- data/test/functional/users_controller_test.rb +19 -0
- data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +68 -0
- data/test/matchers/active_record/allow_value_matcher_test.rb +64 -0
- data/test/matchers/active_record/association_matcher_test.rb +263 -0
- data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +80 -0
- data/test/matchers/active_record/ensure_length_of_matcher_test.rb +158 -0
- data/test/matchers/active_record/have_db_column_matcher_test.rb +169 -0
- data/test/matchers/active_record/have_db_index_matcher_test.rb +91 -0
- data/test/matchers/active_record/have_named_scope_matcher_test.rb +65 -0
- data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +29 -0
- data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +44 -0
- data/test/matchers/active_record/validate_format_of_matcher_test.rb +39 -0
- data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +52 -0
- data/test/matchers/active_record/validate_presence_of_matcher_test.rb +86 -0
- data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +147 -0
- data/test/matchers/controller/assign_to_matcher_test.rb +35 -0
- data/test/matchers/controller/filter_param_matcher_test.rb +32 -0
- data/test/matchers/controller/render_with_layout_matcher_test.rb +33 -0
- data/test/matchers/controller/respond_with_content_type_matcher_test.rb +32 -0
- data/test/matchers/controller/respond_with_matcher_test.rb +106 -0
- data/test/matchers/controller/route_matcher_test.rb +75 -0
- data/test/matchers/controller/set_session_matcher_test.rb +38 -0
- data/test/matchers/controller/set_the_flash_matcher.rb +41 -0
- data/test/model_builder.rb +106 -0
- data/test/other/autoload_macro_test.rb +18 -0
- data/test/other/context_test.rb +203 -0
- data/test/other/convert_to_should_syntax_test.rb +63 -0
- data/test/other/helpers_test.rb +340 -0
- data/test/other/private_helpers_test.rb +32 -0
- data/test/other/should_test.rb +271 -0
- data/test/rails_root/app/controllers/application_controller.rb +25 -0
- data/test/rails_root/app/controllers/posts_controller.rb +87 -0
- data/test/rails_root/app/controllers/users_controller.rb +84 -0
- data/test/rails_root/app/helpers/application_helper.rb +3 -0
- data/test/rails_root/app/helpers/posts_helper.rb +2 -0
- data/test/rails_root/app/helpers/users_helper.rb +2 -0
- data/test/rails_root/app/models/address.rb +7 -0
- data/test/rails_root/app/models/flea.rb +3 -0
- data/test/rails_root/app/models/friendship.rb +4 -0
- data/test/rails_root/app/models/pets/cat.rb +7 -0
- data/test/rails_root/app/models/pets/dog.rb +10 -0
- data/test/rails_root/app/models/post.rb +12 -0
- data/test/rails_root/app/models/product.rb +12 -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/tag.rb +8 -0
- data/test/rails_root/app/models/tagging.rb +4 -0
- data/test/rails_root/app/models/treat.rb +3 -0
- data/test/rails_root/app/models/user.rb +32 -0
- data/test/rails_root/app/views/layouts/posts.rhtml +19 -0
- data/test/rails_root/app/views/layouts/users.rhtml +17 -0
- data/test/rails_root/app/views/layouts/wide.html.erb +1 -0
- data/test/rails_root/app/views/posts/edit.rhtml +27 -0
- data/test/rails_root/app/views/posts/index.rhtml +25 -0
- data/test/rails_root/app/views/posts/new.rhtml +26 -0
- data/test/rails_root/app/views/posts/show.rhtml +18 -0
- data/test/rails_root/app/views/users/edit.rhtml +22 -0
- data/test/rails_root/app/views/users/index.rhtml +22 -0
- data/test/rails_root/app/views/users/new.rhtml +21 -0
- data/test/rails_root/app/views/users/show.rhtml +13 -0
- data/test/rails_root/config/boot.rb +110 -0
- data/test/rails_root/config/database.yml +4 -0
- data/test/rails_root/config/environment.rb +18 -0
- data/test/rails_root/config/environments/test.rb +0 -0
- data/test/rails_root/config/initializers/new_rails_defaults.rb +15 -0
- data/test/rails_root/config/initializers/shoulda.rb +8 -0
- data/test/rails_root/config/routes.rb +6 -0
- data/test/rails_root/db/migrate/001_create_users.rb +19 -0
- data/test/rails_root/db/migrate/002_create_posts.rb +13 -0
- data/test/rails_root/db/migrate/003_create_taggings.rb +12 -0
- data/test/rails_root/db/migrate/004_create_tags.rb +11 -0
- data/test/rails_root/db/migrate/005_create_dogs.rb +12 -0
- data/test/rails_root/db/migrate/006_create_addresses.rb +14 -0
- data/test/rails_root/db/migrate/007_create_fleas.rb +11 -0
- data/test/rails_root/db/migrate/008_create_dogs_fleas.rb +12 -0
- data/test/rails_root/db/migrate/009_create_products.rb +17 -0
- data/test/rails_root/db/migrate/010_create_friendships.rb +14 -0
- data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
- 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/db/schema.rb +0 -0
- data/test/rails_root/log/test.log +8963 -0
- data/test/rails_root/public/404.html +30 -0
- data/test/rails_root/public/422.html +30 -0
- data/test/rails_root/public/500.html +30 -0
- data/test/rails_root/script/console +3 -0
- data/test/rails_root/script/generate +3 -0
- data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
- data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
- data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
- data/test/rspec_test.rb +207 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/address_test.rb +15 -0
- data/test/unit/cat_test.rb +7 -0
- data/test/unit/dog_test.rb +9 -0
- data/test/unit/flea_test.rb +6 -0
- data/test/unit/friendship_test.rb +6 -0
- data/test/unit/post_test.rb +19 -0
- data/test/unit/product_test.rb +23 -0
- data/test/unit/tag_test.rb +15 -0
- data/test/unit/tagging_test.rb +6 -0
- data/test/unit/user_test.rb +80 -0
- 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,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,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
|
+
|