shoulda-matchers 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +116 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +70 -0
- data/Rakefile +50 -0
- data/lib/shoulda-matchers.rb +8 -0
- data/lib/shoulda/matchers/action_controller.rb +38 -0
- data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +114 -0
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +50 -0
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +62 -0
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +54 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +99 -0
- data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +74 -0
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +85 -0
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +93 -0
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +98 -0
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +94 -0
- data/lib/shoulda/matchers/action_mailer.rb +22 -0
- data/lib/shoulda/matchers/action_mailer/have_sent_email.rb +115 -0
- data/lib/shoulda/matchers/active_record.rb +42 -0
- data/lib/shoulda/matchers/active_record/allow_mass_assignment_of_matcher.rb +83 -0
- data/lib/shoulda/matchers/active_record/allow_value_matcher.rb +110 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +226 -0
- data/lib/shoulda/matchers/active_record/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/matchers/active_record/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +169 -0
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +112 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +59 -0
- data/lib/shoulda/matchers/active_record/helpers.rb +34 -0
- data/lib/shoulda/matchers/active_record/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/validate_format_of_matcher.rb +65 -0
- data/lib/shoulda/matchers/active_record/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/matchers/active_record/validate_presence_of_matcher.rb +60 -0
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/matchers/active_record/validation_matcher.rb +56 -0
- data/lib/shoulda/matchers/integrations/rspec.rb +23 -0
- data/lib/shoulda/matchers/integrations/test_unit.rb +41 -0
- data/lib/shoulda/matchers/version.rb +5 -0
- metadata +113 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActionController # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that a session key was set to the expected value.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# it { should set_session(:message) }
|
10
|
+
# it { should set_session(:user_id).to(@user.id) }
|
11
|
+
# it { should_not set_session(:user_id) }
|
12
|
+
def set_session(key)
|
13
|
+
SetSessionMatcher.new(key)
|
14
|
+
end
|
15
|
+
|
16
|
+
class SetSessionMatcher # :nodoc:
|
17
|
+
|
18
|
+
def initialize(key)
|
19
|
+
@key = key.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def to(value = nil, &block)
|
23
|
+
@value = value
|
24
|
+
@value_block = block
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def matches?(controller)
|
29
|
+
@controller = controller
|
30
|
+
@value = @context.instance_eval(&@value_block) if @value_block
|
31
|
+
(assigned_value? && assigned_correct_value?) || cleared_value?
|
32
|
+
end
|
33
|
+
|
34
|
+
def failure_message
|
35
|
+
"Expected #{expectation}, but #{result}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def negative_failure_message
|
39
|
+
"Didn't expect #{expectation}, but #{result}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def description
|
43
|
+
description = "set session variable #{@key.inspect}"
|
44
|
+
description << " to #{@value.inspect}" if defined?(@value)
|
45
|
+
description
|
46
|
+
end
|
47
|
+
|
48
|
+
def in_context(context)
|
49
|
+
@context = context
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def assigned_value?
|
56
|
+
!assigned_value.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def cleared_value?
|
60
|
+
defined?(@value) && @value.nil? && assigned_value.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
def assigned_correct_value?
|
64
|
+
return true if @value.nil?
|
65
|
+
assigned_value == @value
|
66
|
+
end
|
67
|
+
|
68
|
+
def assigned_value
|
69
|
+
session[@key]
|
70
|
+
end
|
71
|
+
|
72
|
+
def session
|
73
|
+
if @controller.request.respond_to?(:session)
|
74
|
+
@controller.request.session.to_hash
|
75
|
+
else
|
76
|
+
@controller.response.session.data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def expectation
|
81
|
+
expectation = "session variable #{@key} to be set"
|
82
|
+
expectation << " to #{@value.inspect}" if @value
|
83
|
+
expectation
|
84
|
+
end
|
85
|
+
|
86
|
+
def result
|
87
|
+
if session.empty?
|
88
|
+
"no session variables were set"
|
89
|
+
else
|
90
|
+
"the session was #{session.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActionController # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the flash contains the given value. Can be a String, a
|
6
|
+
# Regexp, or nil (indicating that the flash should not be set).
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# it { should set_the_flash }
|
11
|
+
# it { should set_the_flash.to("Thank you for placing this order.") }
|
12
|
+
# it { should set_the_flash.to(/created/i) }
|
13
|
+
# it { should set_the_flash.to(/logged in/i).now }
|
14
|
+
# it { should_not set_the_flash }
|
15
|
+
def set_the_flash
|
16
|
+
SetTheFlashMatcher.new
|
17
|
+
end
|
18
|
+
|
19
|
+
class SetTheFlashMatcher # :nodoc:
|
20
|
+
|
21
|
+
def to(value)
|
22
|
+
@value = value
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def now
|
27
|
+
@now = true
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches?(controller)
|
32
|
+
@controller = controller
|
33
|
+
sets_the_flash? && string_value_matches? && regexp_value_matches?
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :failure_message, :negative_failure_message
|
37
|
+
|
38
|
+
def description
|
39
|
+
description = "set the flash"
|
40
|
+
description << " to #{@value.inspect}" unless @value.nil?
|
41
|
+
description
|
42
|
+
end
|
43
|
+
|
44
|
+
def failure_message
|
45
|
+
"Expected #{expectation}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def negative_failure_message
|
49
|
+
"Did not expect #{expectation}"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def sets_the_flash?
|
55
|
+
!flash.blank?
|
56
|
+
end
|
57
|
+
|
58
|
+
def string_value_matches?
|
59
|
+
return true unless String === @value
|
60
|
+
flash.values.any? {|value| value == @value }
|
61
|
+
end
|
62
|
+
|
63
|
+
def regexp_value_matches?
|
64
|
+
return true unless Regexp === @value
|
65
|
+
flash.values.any? {|value| value =~ @value }
|
66
|
+
end
|
67
|
+
|
68
|
+
def flash
|
69
|
+
return @flash if @flash
|
70
|
+
@flash = @controller.flash.dup
|
71
|
+
@flash.sweep unless @now
|
72
|
+
@flash
|
73
|
+
end
|
74
|
+
|
75
|
+
def expectation
|
76
|
+
expectation = "the flash#{".now" if @now} to be set"
|
77
|
+
expectation << " to #{@value.inspect}" unless @value.nil?
|
78
|
+
expectation << ", but #{flash_description}"
|
79
|
+
expectation
|
80
|
+
end
|
81
|
+
|
82
|
+
def flash_description
|
83
|
+
if flash.blank?
|
84
|
+
"no flash was set"
|
85
|
+
else
|
86
|
+
"was #{flash.inspect}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'shoulda/matchers/action_mailer/have_sent_email'
|
2
|
+
|
3
|
+
module Shoulda
|
4
|
+
module Matchers
|
5
|
+
# = Matchers for your mailers
|
6
|
+
#
|
7
|
+
# This matcher will test that email is sent properly
|
8
|
+
#
|
9
|
+
# describe User do
|
10
|
+
# it { should have_sent_email.with_subject(/is spam$/) }
|
11
|
+
# it { should have_sent_email.from('do-not-reply@example.com') }
|
12
|
+
# it { should have_sent_email.with_body(/is spam\./) }
|
13
|
+
# it { should have_sent_email.to('myself@me.com') }
|
14
|
+
# it { should have_sent_email.with_subject(/spam/).
|
15
|
+
# from('do-not-reply@example.com').
|
16
|
+
# with_body(/spam/).
|
17
|
+
# to('myself@me.com') }
|
18
|
+
# end
|
19
|
+
module ActionMailer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActionMailer # :nodoc:
|
4
|
+
|
5
|
+
# The right email is sent.
|
6
|
+
#
|
7
|
+
# it { should have_sent_email.with_subject(/is spam$/) }
|
8
|
+
# it { should have_sent_email.from('do-not-reply@example.com') }
|
9
|
+
# it { should have_sent_email.with_body(/is spam\./) }
|
10
|
+
# it { should have_sent_email.to('myself@me.com') }
|
11
|
+
# it { should have_sent_email.with_subject(/spam/).
|
12
|
+
# from('do-not-reply@example.com').
|
13
|
+
# with_body(/spam/).
|
14
|
+
# to('myself@me.com') }
|
15
|
+
def have_sent_email
|
16
|
+
HaveSentEmailMatcher.new
|
17
|
+
end
|
18
|
+
|
19
|
+
class HaveSentEmailMatcher # :nodoc:
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_subject(email_subject)
|
25
|
+
@email_subject = email_subject
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def from(sender)
|
30
|
+
@sender = sender
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def with_body(body)
|
35
|
+
@body = body
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def to(recipient)
|
40
|
+
@recipient = recipient
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def matches?(subject)
|
45
|
+
::ActionMailer::Base.deliveries.each do |mail|
|
46
|
+
@subject_failed = !regexp_or_string_match(mail.subject, @email_subject) if @email_subject
|
47
|
+
@body_failed = !regexp_or_string_match(mail.body, @body) if @body
|
48
|
+
@sender_failed = !regexp_or_string_match_in_array(mail.from, @sender) if @sender
|
49
|
+
@recipient_failed = !regexp_or_string_match_in_array(mail.to, @recipient) if @recipient
|
50
|
+
return true unless anything_failed?
|
51
|
+
end
|
52
|
+
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def failure_message
|
57
|
+
"Expected #{expectation}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def negative_failure_message
|
61
|
+
"Did not expect #{expectation}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def description
|
65
|
+
description = "send an email"
|
66
|
+
description << " with a subject of #{@email_subject.inspect}" if @email_subject
|
67
|
+
description << " containing #{@body.inspect}" if @body
|
68
|
+
description << " from #{@sender.inspect}" if @sender
|
69
|
+
description << " to #{@recipient.inspect}" if @recipient
|
70
|
+
description
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def expectation
|
76
|
+
expectation = "sent email"
|
77
|
+
expectation << " with subject #{@email_subject.inspect}" if @subject_failed
|
78
|
+
expectation << " with body #{@body.inspect}" if @body_failed
|
79
|
+
expectation << " from #{@sender.inspect}" if @sender_failed
|
80
|
+
expectation << " to #{@recipient.inspect}" if @recipient_failed
|
81
|
+
expectation << "\nDeliveries:\n#{inspect_deliveries}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def inspect_deliveries
|
85
|
+
::ActionMailer::Base.deliveries.map do |delivery|
|
86
|
+
"#{delivery.subject.inspect} to #{delivery.to.inspect}"
|
87
|
+
end.join("\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
def anything_failed?
|
91
|
+
@subject_failed || @body_failed || @sender_failed || @recipient_failed
|
92
|
+
end
|
93
|
+
|
94
|
+
def regexp_or_string_match(a_string, a_regexp_or_string)
|
95
|
+
case a_regexp_or_string
|
96
|
+
when Regexp
|
97
|
+
a_string =~ a_regexp_or_string
|
98
|
+
when String
|
99
|
+
a_string == a_regexp_or_string
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def regexp_or_string_match_in_array(an_array, a_regexp_or_string)
|
104
|
+
case a_regexp_or_string
|
105
|
+
when Regexp
|
106
|
+
an_array.any? { |string| string =~ a_regexp_or_string }
|
107
|
+
when String
|
108
|
+
an_array.include?(a_regexp_or_string)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'shoulda/matchers/active_record/helpers'
|
2
|
+
require 'shoulda/matchers/active_record/validation_matcher'
|
3
|
+
require 'shoulda/matchers/active_record/allow_value_matcher'
|
4
|
+
require 'shoulda/matchers/active_record/ensure_length_of_matcher'
|
5
|
+
require 'shoulda/matchers/active_record/ensure_inclusion_of_matcher'
|
6
|
+
require 'shoulda/matchers/active_record/validate_presence_of_matcher'
|
7
|
+
require 'shoulda/matchers/active_record/validate_format_of_matcher'
|
8
|
+
require 'shoulda/matchers/active_record/validate_uniqueness_of_matcher'
|
9
|
+
require 'shoulda/matchers/active_record/validate_acceptance_of_matcher'
|
10
|
+
require 'shoulda/matchers/active_record/validate_numericality_of_matcher'
|
11
|
+
require 'shoulda/matchers/active_record/association_matcher'
|
12
|
+
require 'shoulda/matchers/active_record/have_db_column_matcher'
|
13
|
+
require 'shoulda/matchers/active_record/have_db_index_matcher'
|
14
|
+
require 'shoulda/matchers/active_record/have_readonly_attribute_matcher'
|
15
|
+
require 'shoulda/matchers/active_record/allow_mass_assignment_of_matcher'
|
16
|
+
|
17
|
+
|
18
|
+
module Shoulda
|
19
|
+
module Matchers
|
20
|
+
# = Matchers for your active record models
|
21
|
+
#
|
22
|
+
# These matchers will test most of the validations and associations for your
|
23
|
+
# ActiveRecord models.
|
24
|
+
#
|
25
|
+
# describe User do
|
26
|
+
# it { should validate_presence_of(:name) }
|
27
|
+
# it { should validate_presence_of(:phone_number) }
|
28
|
+
# %w(abcd 1234).each do |value|
|
29
|
+
# it { should_not allow_value(value).for(:phone_number) }
|
30
|
+
# end
|
31
|
+
# it { should allow_value("(123) 456-7890").for(:phone_number) }
|
32
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
33
|
+
# it { should have_one(:profile) }
|
34
|
+
# it { should have_many(:dogs) }
|
35
|
+
# it { should have_many(:messes).through(:dogs) }
|
36
|
+
# it { should belong_to(:lover) }
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
module ActiveRecord
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the attribute can be set on mass update.
|
6
|
+
#
|
7
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
8
|
+
# it { should allow_mass_assignment_of(:first_name) }
|
9
|
+
#
|
10
|
+
def allow_mass_assignment_of(value)
|
11
|
+
AllowMassAssignmentOfMatcher.new(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
class AllowMassAssignmentOfMatcher # :nodoc:
|
15
|
+
|
16
|
+
def initialize(attribute)
|
17
|
+
@attribute = attribute.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(subject)
|
21
|
+
@subject = subject
|
22
|
+
if attr_mass_assignable?
|
23
|
+
if whitelisting?
|
24
|
+
@negative_failure_message = "#{@attribute} was made accessible"
|
25
|
+
else
|
26
|
+
if protected_attributes.empty?
|
27
|
+
@negative_failure_message = "no attributes were protected"
|
28
|
+
else
|
29
|
+
@negative_failure_message = "#{class_name} is protecting " <<
|
30
|
+
"#{protected_attributes.to_a.to_sentence}, " <<
|
31
|
+
"but not #{@attribute}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
true
|
35
|
+
else
|
36
|
+
if whitelisting?
|
37
|
+
@failure_message =
|
38
|
+
"Expected #{@attribute} to be accessible"
|
39
|
+
else
|
40
|
+
@failure_message =
|
41
|
+
"Did not expect #{@attribute} to be protected"
|
42
|
+
end
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :failure_message, :negative_failure_message
|
48
|
+
|
49
|
+
def description
|
50
|
+
"allow mass assignment of #{@attribute}"
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def protected_attributes
|
56
|
+
@protected_attributes ||= (@subject.class.protected_attributes || [])
|
57
|
+
end
|
58
|
+
|
59
|
+
def accessible_attributes
|
60
|
+
@accessible_attributes ||= (@subject.class.accessible_attributes || [])
|
61
|
+
end
|
62
|
+
|
63
|
+
def whitelisting?
|
64
|
+
!accessible_attributes.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def attr_mass_assignable?
|
68
|
+
if whitelisting?
|
69
|
+
accessible_attributes.include?(@attribute)
|
70
|
+
else
|
71
|
+
!protected_attributes.include?(@attribute)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def class_name
|
76
|
+
@subject.class.name
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|