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,148 @@
|
|
|
1
|
+
module Shoulda # :nodoc:
|
|
2
|
+
module ActiveRecord # :nodoc:
|
|
3
|
+
module Matchers
|
|
4
|
+
|
|
5
|
+
# Ensures that the model is invalid if the given attribute is not unique.
|
|
6
|
+
#
|
|
7
|
+
# Internally, this uses values from existing records to test validations,
|
|
8
|
+
# so this will always fail if you have not saved at least one record for
|
|
9
|
+
# the model being tested, like so:
|
|
10
|
+
#
|
|
11
|
+
# describe User do
|
|
12
|
+
# before(:each) { User.create!(:email => 'address@example.com') }
|
|
13
|
+
# it { should validate_uniqueness_of(:email) }
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# Options:
|
|
17
|
+
#
|
|
18
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
|
19
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
|
20
|
+
# Defaults to the translation for <tt>:taken</tt>.
|
|
21
|
+
# * <tt>scoped_to</tt> - field(s) to scope the uniqueness to.
|
|
22
|
+
# * <tt>case_insensitive</tt> - ensures that the validation does not
|
|
23
|
+
# check case. Off by default. Ignored by non-text attributes.
|
|
24
|
+
#
|
|
25
|
+
# Examples:
|
|
26
|
+
# it { should validate_uniqueness_of(:keyword) }
|
|
27
|
+
# it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
|
|
28
|
+
# it { should validate_uniqueness_of(:email).scoped_to(:name) }
|
|
29
|
+
# it { should validate_uniqueness_of(:email).
|
|
30
|
+
# scoped_to(:first_name, :last_name) }
|
|
31
|
+
# it { should validate_uniqueness_of(:keyword).case_insensitive }
|
|
32
|
+
#
|
|
33
|
+
def validate_uniqueness_of(attr)
|
|
34
|
+
ValidateUniquenessOfMatcher.new(attr)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class ValidateUniquenessOfMatcher < ValidationMatcher # :nodoc:
|
|
38
|
+
include Helpers
|
|
39
|
+
|
|
40
|
+
def initialize(attribute)
|
|
41
|
+
@attribute = attribute
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def scoped_to(*scopes)
|
|
45
|
+
@scopes = [*scopes].flatten
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def with_message(message)
|
|
50
|
+
@expected_message = message
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def case_insensitive
|
|
55
|
+
@case_insensitive = true
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def description
|
|
60
|
+
result = "require "
|
|
61
|
+
result << "case sensitive " unless @case_insensitive
|
|
62
|
+
result << "unique value for #{@attribute}"
|
|
63
|
+
result << " scoped to #{@scopes.join(', ')}" unless @scopes.blank?
|
|
64
|
+
result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def matches?(subject)
|
|
68
|
+
@subject = subject.class.new
|
|
69
|
+
@expected_message ||= :taken
|
|
70
|
+
find_existing &&
|
|
71
|
+
set_scoped_attributes &&
|
|
72
|
+
validate_attribute &&
|
|
73
|
+
validate_after_scope_change
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def find_existing
|
|
79
|
+
if @existing = @subject.class.find(:first)
|
|
80
|
+
true
|
|
81
|
+
else
|
|
82
|
+
@failure_message = "Can't find first #{class_name}"
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def set_scoped_attributes
|
|
88
|
+
unless @scopes.blank?
|
|
89
|
+
@scopes.each do |scope|
|
|
90
|
+
setter = :"#{scope}="
|
|
91
|
+
unless @subject.respond_to?(setter)
|
|
92
|
+
@failure_message =
|
|
93
|
+
"#{class_name} doesn't seem to have a #{scope} attribute."
|
|
94
|
+
return false
|
|
95
|
+
end
|
|
96
|
+
@subject.send("#{scope}=", @existing.send(scope))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def validate_attribute
|
|
103
|
+
disallows_value_of(existing_value, @expected_message)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# TODO: There is a chance that we could change the scoped field
|
|
107
|
+
# to a value that's already taken. An alternative implementation
|
|
108
|
+
# could actually find all values for scope and create a unique
|
|
109
|
+
def validate_after_scope_change
|
|
110
|
+
if @scopes.blank?
|
|
111
|
+
true
|
|
112
|
+
else
|
|
113
|
+
@scopes.all? do |scope|
|
|
114
|
+
previous_value = @existing.send(scope)
|
|
115
|
+
|
|
116
|
+
# Assume the scope is a foreign key if the field is nil
|
|
117
|
+
previous_value ||= 0
|
|
118
|
+
|
|
119
|
+
next_value = previous_value.next
|
|
120
|
+
|
|
121
|
+
@subject.send("#{scope}=", next_value)
|
|
122
|
+
|
|
123
|
+
if allows_value_of(existing_value, @expected_message)
|
|
124
|
+
@negative_failure_message <<
|
|
125
|
+
" (with different value of #{scope})"
|
|
126
|
+
true
|
|
127
|
+
else
|
|
128
|
+
@failure_message << " (with different value of #{scope})"
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def class_name
|
|
136
|
+
@subject.class.name
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def existing_value
|
|
140
|
+
value = @existing.send(@attribute)
|
|
141
|
+
value.swapcase! if @case_insensitive && value.respond_to?(:swapcase!)
|
|
142
|
+
value
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Shoulda # :nodoc:
|
|
2
|
+
module ActiveRecord # :nodoc:
|
|
3
|
+
module Matchers
|
|
4
|
+
|
|
5
|
+
class ValidationMatcher # :nodoc:
|
|
6
|
+
|
|
7
|
+
attr_reader :failure_message
|
|
8
|
+
|
|
9
|
+
def initialize(attribute)
|
|
10
|
+
@attribute = attribute
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def negative_failure_message
|
|
14
|
+
@negative_failure_message || @failure_message
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def matches?(subject)
|
|
18
|
+
@subject = subject
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def allows_value_of(value, message = nil)
|
|
26
|
+
allow = AllowValueMatcher.
|
|
27
|
+
new(value).
|
|
28
|
+
for(@attribute).
|
|
29
|
+
with_message(message)
|
|
30
|
+
if allow.matches?(@subject)
|
|
31
|
+
@negative_failure_message = allow.failure_message
|
|
32
|
+
true
|
|
33
|
+
else
|
|
34
|
+
@failure_message = allow.negative_failure_message
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def disallows_value_of(value, message = nil)
|
|
40
|
+
disallow = AllowValueMatcher.
|
|
41
|
+
new(value).
|
|
42
|
+
for(@attribute).
|
|
43
|
+
with_message(message)
|
|
44
|
+
if disallow.matches?(@subject)
|
|
45
|
+
@failure_message = disallow.negative_failure_message
|
|
46
|
+
false
|
|
47
|
+
else
|
|
48
|
+
@negative_failure_message = disallow.failure_message
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'shoulda/active_record/helpers'
|
|
2
|
+
require 'shoulda/active_record/matchers/validation_matcher'
|
|
3
|
+
require 'shoulda/active_record/matchers/allow_value_matcher'
|
|
4
|
+
require 'shoulda/active_record/matchers/ensure_length_of_matcher'
|
|
5
|
+
require 'shoulda/active_record/matchers/ensure_inclusion_of_matcher'
|
|
6
|
+
require 'shoulda/active_record/matchers/validate_presence_of_matcher'
|
|
7
|
+
require 'shoulda/active_record/matchers/validate_format_of_matcher'
|
|
8
|
+
require 'shoulda/active_record/matchers/validate_uniqueness_of_matcher'
|
|
9
|
+
require 'shoulda/active_record/matchers/validate_acceptance_of_matcher'
|
|
10
|
+
require 'shoulda/active_record/matchers/validate_numericality_of_matcher'
|
|
11
|
+
require 'shoulda/active_record/matchers/association_matcher'
|
|
12
|
+
require 'shoulda/active_record/matchers/have_db_column_matcher'
|
|
13
|
+
require 'shoulda/active_record/matchers/have_db_index_matcher'
|
|
14
|
+
require 'shoulda/active_record/matchers/have_readonly_attribute_matcher'
|
|
15
|
+
require 'shoulda/active_record/matchers/allow_mass_assignment_of_matcher'
|
|
16
|
+
require 'shoulda/active_record/matchers/have_named_scope_matcher'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
module Shoulda # :nodoc:
|
|
20
|
+
module ActiveRecord # :nodoc:
|
|
21
|
+
# = Matchers for your active record models
|
|
22
|
+
#
|
|
23
|
+
# These matchers will test most of the validations and associations for your
|
|
24
|
+
# ActiveRecord models.
|
|
25
|
+
#
|
|
26
|
+
# describe User do
|
|
27
|
+
# it { should validate_presence_of(:name) }
|
|
28
|
+
# it { should validate_presence_of(:phone_number) }
|
|
29
|
+
# %w(abcd 1234).each do |value|
|
|
30
|
+
# it { should_not allow_value(value).for(:phone_number) }
|
|
31
|
+
# end
|
|
32
|
+
# it { should allow_value("(123) 456-7890").for(:phone_number) }
|
|
33
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
|
34
|
+
# it { should have_one(:profile) }
|
|
35
|
+
# it { should have_many(:dogs) }
|
|
36
|
+
# it { should have_many(:messes).through(:dogs) }
|
|
37
|
+
# it { should belong_to(:lover) }
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
module Matchers
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'shoulda'
|
|
2
|
+
require 'shoulda/active_record/helpers'
|
|
3
|
+
require 'shoulda/active_record/matchers'
|
|
4
|
+
require 'shoulda/active_record/assertions'
|
|
5
|
+
require 'shoulda/active_record/macros'
|
|
6
|
+
|
|
7
|
+
module Test # :nodoc: all
|
|
8
|
+
module Unit
|
|
9
|
+
class TestCase
|
|
10
|
+
include Shoulda::ActiveRecord::Helpers
|
|
11
|
+
include Shoulda::ActiveRecord::Matchers
|
|
12
|
+
include Shoulda::ActiveRecord::Assertions
|
|
13
|
+
extend Shoulda::ActiveRecord::Macros
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Shoulda # :nodoc:
|
|
2
|
+
module Assertions
|
|
3
|
+
# Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
|
|
4
|
+
#
|
|
5
|
+
# assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
|
|
6
|
+
def assert_same_elements(a1, a2, msg = nil)
|
|
7
|
+
[:select, :inject, :size].each do |m|
|
|
8
|
+
[a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
|
|
12
|
+
assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
|
|
13
|
+
|
|
14
|
+
assert_equal(a1h, a2h, msg)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Asserts that the given collection contains item x. If x is a regular expression, ensure that
|
|
18
|
+
# at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
|
|
19
|
+
#
|
|
20
|
+
# assert_contains(['a', '1'], /\d/) => passes
|
|
21
|
+
# assert_contains(['a', '1'], 'a') => passes
|
|
22
|
+
# assert_contains(['a', '1'], /not there/) => fails
|
|
23
|
+
def assert_contains(collection, x, extra_msg = "")
|
|
24
|
+
collection = [collection] unless collection.is_a?(Array)
|
|
25
|
+
msg = "#{x.inspect} not found in #{collection.to_a.inspect} #{extra_msg}"
|
|
26
|
+
case x
|
|
27
|
+
when Regexp
|
|
28
|
+
assert(collection.detect { |e| e =~ x }, msg)
|
|
29
|
+
else
|
|
30
|
+
assert(collection.include?(x), msg)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
|
|
35
|
+
# none of the elements from the collection match x.
|
|
36
|
+
def assert_does_not_contain(collection, x, extra_msg = "")
|
|
37
|
+
collection = [collection] unless collection.is_a?(Array)
|
|
38
|
+
msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
|
|
39
|
+
case x
|
|
40
|
+
when Regexp
|
|
41
|
+
assert(!collection.detect { |e| e =~ x }, msg)
|
|
42
|
+
else
|
|
43
|
+
assert(!collection.include?(x), msg)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Asserts that the given matcher returns true when +target+ is passed to #matches?
|
|
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
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Asserts that the given matcher returns false when +target+ is passed to #matches?
|
|
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
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Shoulda # :nodoc:
|
|
2
|
+
# Call autoload_macros when you want to load test macros automatically in a non-Rails
|
|
3
|
+
# project (it's done automatically for Rails projects).
|
|
4
|
+
# You don't need to specify ROOT/test/shoulda_macros explicitly. Your custom macros
|
|
5
|
+
# are loaded automatically when you call autoload_macros.
|
|
6
|
+
#
|
|
7
|
+
# The first argument is the path to you application's root directory.
|
|
8
|
+
# All following arguments are directories relative to your root, which contain
|
|
9
|
+
# shoulda_macros subdirectories. These directories support the same kinds of globs as the
|
|
10
|
+
# Dir class.
|
|
11
|
+
#
|
|
12
|
+
# Basic usage (from a test_helper):
|
|
13
|
+
# Shoulda.autoload_macros(File.dirname(__FILE__) + '/..')
|
|
14
|
+
# will load everything in
|
|
15
|
+
# - your_app/test/shoulda_macros
|
|
16
|
+
#
|
|
17
|
+
# To load vendored macros as well:
|
|
18
|
+
# Shoulda.autoload_macros(APP_ROOT, 'vendor/*')
|
|
19
|
+
# will load everything in
|
|
20
|
+
# - APP_ROOT/vendor/*/shoulda_macros
|
|
21
|
+
# - APP_ROOT/test/shoulda_macros
|
|
22
|
+
#
|
|
23
|
+
# To load macros in an app with a vendor directory laid out like Rails':
|
|
24
|
+
# Shoulda.autoload_macros(APP_ROOT, 'vendor/{plugins,gems}/*')
|
|
25
|
+
# or
|
|
26
|
+
# Shoulda.autoload_macros(APP_ROOT, 'vendor/plugins/*', 'vendor/gems/*')
|
|
27
|
+
# will load everything in
|
|
28
|
+
# - APP_ROOT/vendor/plugins/*/shoulda_macros
|
|
29
|
+
# - APP_ROOT/vendor/gems/*/shoulda_macros
|
|
30
|
+
# - APP_ROOT/test/shoulda_macros
|
|
31
|
+
#
|
|
32
|
+
# If you prefer to stick testing dependencies away from your production dependencies:
|
|
33
|
+
# Shoulda.autoload_macros(APP_ROOT, 'vendor/*', 'test/vendor/*')
|
|
34
|
+
# will load everything in
|
|
35
|
+
# - APP_ROOT/vendor/*/shoulda_macros
|
|
36
|
+
# - APP_ROOT/test/vendor/*/shoulda_macros
|
|
37
|
+
# - APP_ROOT/test/shoulda_macros
|
|
38
|
+
def self.autoload_macros(root, *dirs)
|
|
39
|
+
dirs << File.join('test')
|
|
40
|
+
complete_dirs = dirs.map{|d| File.join(root, d, 'shoulda_macros')}
|
|
41
|
+
all_files = complete_dirs.inject([]){ |files, dir| files + Dir[File.join(dir, '*.rb')] }
|
|
42
|
+
all_files.each do |file|
|
|
43
|
+
require file
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|