shoulda 2.0.6 → 2.9.0

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 (84) hide show
  1. data/README.rdoc +35 -7
  2. data/Rakefile +5 -3
  3. data/lib/shoulda.rb +7 -15
  4. data/lib/shoulda/action_mailer.rb +1 -1
  5. data/lib/shoulda/action_mailer/assertions.rb +32 -33
  6. data/lib/shoulda/active_record.rb +6 -2
  7. data/lib/shoulda/active_record/assertions.rb +62 -81
  8. data/lib/shoulda/active_record/helpers.rb +40 -0
  9. data/lib/shoulda/active_record/macros.rb +518 -639
  10. data/lib/shoulda/active_record/matchers.rb +42 -0
  11. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  12. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  13. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  14. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  15. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  16. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  17. data/lib/shoulda/active_record/matchers/have_index_matcher.rb +105 -0
  18. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +125 -0
  19. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  20. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  21. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  22. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  23. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  24. data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
  25. data/lib/shoulda/assertions.rb +50 -40
  26. data/lib/shoulda/autoload_macros.rb +46 -0
  27. data/lib/shoulda/context.rb +124 -126
  28. data/lib/shoulda/controller.rb +8 -8
  29. data/lib/shoulda/controller/formats/html.rb +158 -160
  30. data/lib/shoulda/controller/formats/xml.rb +132 -134
  31. data/lib/shoulda/controller/helpers.rb +51 -53
  32. data/lib/shoulda/controller/macros.rb +278 -258
  33. data/lib/shoulda/controller/resource_options.rb +211 -214
  34. data/lib/shoulda/helpers.rb +5 -7
  35. data/lib/shoulda/macros.rb +63 -64
  36. data/lib/shoulda/private_helpers.rb +16 -18
  37. data/lib/shoulda/rails.rb +1 -8
  38. data/lib/shoulda/rspec.rb +5 -0
  39. data/lib/shoulda/tasks/list_tests.rake +6 -1
  40. data/lib/shoulda/test_unit.rb +19 -0
  41. data/rails/init.rb +1 -1
  42. data/test/README +2 -2
  43. data/test/fail_macros.rb +16 -16
  44. data/test/functional/posts_controller_test.rb +5 -2
  45. data/test/matchers/allow_mass_assignment_of_matcher_test.rb +68 -0
  46. data/test/matchers/allow_value_matcher_test.rb +41 -0
  47. data/test/matchers/association_matcher_test.rb +258 -0
  48. data/test/matchers/ensure_inclusion_of_matcher_test.rb +80 -0
  49. data/test/matchers/ensure_length_of_matcher_test.rb +158 -0
  50. data/test/matchers/have_db_column_matcher_test.rb +169 -0
  51. data/test/matchers/have_index_matcher_test.rb +74 -0
  52. data/test/matchers/have_named_scope_matcher_test.rb +65 -0
  53. data/test/matchers/have_readonly_attributes_matcher_test.rb +29 -0
  54. data/test/matchers/validate_acceptance_of_matcher_test.rb +44 -0
  55. data/test/matchers/validate_numericality_of_matcher_test.rb +52 -0
  56. data/test/matchers/validate_presence_of_matcher_test.rb +86 -0
  57. data/test/matchers/validate_uniqueness_of_matcher_test.rb +141 -0
  58. data/test/model_builder.rb +61 -0
  59. data/test/other/autoload_macro_test.rb +18 -0
  60. data/test/other/helpers_test.rb +58 -0
  61. data/test/other/private_helpers_test.rb +1 -1
  62. data/test/other/should_test.rb +16 -16
  63. data/test/rails_root/app/controllers/posts_controller.rb +6 -5
  64. data/test/rails_root/app/models/pets/dog.rb +10 -0
  65. data/test/rails_root/app/models/treat.rb +3 -0
  66. data/test/rails_root/app/models/user.rb +2 -2
  67. data/test/rails_root/app/views/layouts/posts.rhtml +2 -0
  68. data/test/rails_root/config/database.yml +1 -1
  69. data/test/rails_root/config/environments/{sqlite3.rb → test.rb} +0 -0
  70. data/test/rails_root/db/migrate/001_create_users.rb +3 -2
  71. data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
  72. data/test/rails_root/log/test.log +0 -0
  73. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  74. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  75. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  76. data/test/test_helper.rb +3 -1
  77. data/test/unit/address_test.rb +1 -1
  78. data/test/unit/dog_test.rb +5 -2
  79. data/test/unit/post_test.rb +7 -3
  80. data/test/unit/product_test.rb +2 -2
  81. data/test/unit/tag_test.rb +2 -1
  82. data/test/unit/user_test.rb +17 -8
  83. metadata +44 -4
  84. data/test/rails_root/app/models/dog.rb +0 -5
@@ -0,0 +1,169 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class HaveDbColumnMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "have_db_column" do
6
+ setup do
7
+ @matcher = have_db_column(:nickname)
8
+ end
9
+
10
+ should "accept an existing database column" do
11
+ create_table 'superheros' do |table|
12
+ table.string :nickname
13
+ end
14
+ define_model_class 'Superhero'
15
+ assert_accepts @matcher, Superhero.new
16
+ end
17
+
18
+ should "reject a nonexistent database column" do
19
+ define_model :superhero
20
+ assert_rejects @matcher, Superhero.new
21
+ end
22
+ end
23
+
24
+ context "have_db_column of type string" do
25
+ setup do
26
+ @matcher = have_db_column(:nickname).of_type(:string)
27
+ end
28
+
29
+ should "accept a column of correct type" do
30
+ create_table 'superheros' do |table|
31
+ table.string :nickname
32
+ end
33
+ define_model_class 'Superhero'
34
+ assert_accepts @matcher, Superhero.new
35
+ end
36
+
37
+ should "reject a nonexistent database column" do
38
+ define_model :superhero
39
+ assert_rejects @matcher, Superhero.new
40
+ end
41
+
42
+ should "reject a column of wrong type" do
43
+ create_table 'superheros' do |table|
44
+ table.integer :nickname
45
+ end
46
+ define_model_class 'Superhero'
47
+ assert_rejects @matcher, Superhero.new
48
+ end
49
+ end
50
+
51
+ context "have_db_column with precision option" do
52
+ setup do
53
+ @matcher = have_db_column(:salary).with_options(:precision => 5)
54
+ end
55
+
56
+ should "accept a column of correct precision" do
57
+ create_table 'superheros' do |table|
58
+ table.decimal :salary, :precision => 5
59
+ end
60
+ define_model_class 'Superhero'
61
+ assert_accepts @matcher, Superhero.new
62
+ end
63
+
64
+ should "reject a column of wrong precision" do
65
+ create_table 'superheros' do |table|
66
+ table.decimal :salary, :precision => 15
67
+ end
68
+ define_model_class 'Superhero'
69
+ assert_rejects @matcher, Superhero.new
70
+ end
71
+ end
72
+
73
+ context "have_db_column with limit option" do
74
+ setup do
75
+ @matcher = have_db_column(:email).
76
+ of_type(:string).
77
+ with_options(:limit => 255)
78
+ end
79
+
80
+ should "accept a column of correct limit" do
81
+ create_table 'superheros' do |table|
82
+ table.string :email, :limit => 255
83
+ end
84
+ define_model_class 'Superhero'
85
+ assert_accepts @matcher, Superhero.new
86
+ end
87
+
88
+ should "reject a column of wrong limit" do
89
+ create_table 'superheros' do |table|
90
+ table.string :email, :limit => 500
91
+ end
92
+ define_model_class 'Superhero'
93
+ assert_rejects @matcher, Superhero.new
94
+ end
95
+ end
96
+
97
+ context "have_db_column with default option" do
98
+ setup do
99
+ @matcher = have_db_column(:admin).
100
+ of_type(:boolean).
101
+ with_options(:default => false)
102
+ end
103
+
104
+ should "accept a column of correct default" do
105
+ create_table 'superheros' do |table|
106
+ table.boolean :admin, :default => false
107
+ end
108
+ define_model_class 'Superhero'
109
+ assert_accepts @matcher, Superhero.new
110
+ end
111
+
112
+ should "reject a column of wrong default" do
113
+ create_table 'superheros' do |table|
114
+ table.boolean :admin, :default => true
115
+ end
116
+ define_model_class 'Superhero'
117
+ assert_rejects @matcher, Superhero.new
118
+ end
119
+ end
120
+
121
+ context "have_db_column with null option" do
122
+ setup do
123
+ @matcher = have_db_column(:admin).
124
+ of_type(:boolean).
125
+ with_options(:null => false)
126
+ end
127
+
128
+ should "accept a column of correct null" do
129
+ create_table 'superheros' do |table|
130
+ table.boolean :admin, :null => false
131
+ end
132
+ define_model_class 'Superhero'
133
+ assert_accepts @matcher, Superhero.new
134
+ end
135
+
136
+ should "reject a column of wrong null" do
137
+ create_table 'superheros' do |table|
138
+ table.boolean :admin, :null => true
139
+ end
140
+ define_model_class 'Superhero'
141
+ assert_rejects @matcher, Superhero.new
142
+ end
143
+ end
144
+
145
+ context "have_db_column with scale option" do
146
+ setup do
147
+ @matcher = have_db_column(:salary).
148
+ of_type(:decimal).
149
+ with_options(:scale => 2)
150
+ end
151
+
152
+ should "accept a column of correct scale" do
153
+ create_table 'superheros' do |table|
154
+ table.decimal :salary, :precision => 10, :scale => 2
155
+ end
156
+ define_model_class 'Superhero'
157
+ assert_accepts @matcher, Superhero.new
158
+ end
159
+
160
+ should "reject a column of wrong scale" do
161
+ create_table 'superheros' do |table|
162
+ table.decimal :salary, :precision => 10, :scale => 4
163
+ end
164
+ define_model_class 'Superhero'
165
+ assert_rejects @matcher, Superhero.new
166
+ end
167
+ end
168
+
169
+ end
@@ -0,0 +1,74 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class HaveIndexMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "have_index" do
6
+ setup do
7
+ @matcher = have_index(:age)
8
+ end
9
+
10
+ should "accept an existing index" do
11
+ db_connection = create_table 'superheros' do |table|
12
+ table.integer :age
13
+ end
14
+ db_connection.add_index :superheros, :age
15
+ define_model_class 'Superhero'
16
+ assert_accepts @matcher, Superhero.new
17
+ end
18
+
19
+ should "reject a nonexistent index" do
20
+ define_model :superhero
21
+ assert_rejects @matcher, Superhero.new
22
+ end
23
+ end
24
+
25
+ context "have_index with unique option" do
26
+ setup do
27
+ @matcher = have_index(:ssn).unique(true)
28
+ end
29
+
30
+ should "accept an index of correct unique" do
31
+ db_connection = create_table 'superheros' do |table|
32
+ table.integer :ssn
33
+ end
34
+ db_connection.add_index :superheros, :ssn, :unique => true
35
+ define_model_class 'Superhero'
36
+ assert_accepts @matcher, Superhero.new
37
+ end
38
+
39
+ should "reject an index of wrong unique" do
40
+ db_connection = create_table 'superheros' do |table|
41
+ table.integer :ssn
42
+ end
43
+ db_connection.add_index :superheros, :ssn, :unique => false
44
+ define_model_class 'Superhero'
45
+ assert_rejects @matcher, Superhero.new
46
+ end
47
+ end
48
+
49
+ context "have_index on multiple columns" do
50
+ setup do
51
+ @matcher = have_index([:geocodable_type, :geocodable_id])
52
+ end
53
+
54
+ should "accept an existing index" do
55
+ db_connection = create_table 'geocodings' do |table|
56
+ table.integer :geocodable_id
57
+ table.string :geocodable_type
58
+ end
59
+ db_connection.add_index :geocodings, [:geocodable_type, :geocodable_id]
60
+ define_model_class 'Geocoding'
61
+ assert_accepts @matcher, Geocoding.new
62
+ end
63
+
64
+ should "reject a nonexistant index" do
65
+ db_connection = create_table 'geocodings' do |table|
66
+ table.integer :geocodable_id
67
+ table.string :geocodable_type
68
+ end
69
+ define_model_class 'Geocoding'
70
+ assert_rejects @matcher, Geocoding.new
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class HaveNamedScopeMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "an attribute with a named scope" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ named_scope :xyz, lambda {|n|
9
+ { :order => :attr }
10
+ }
11
+ end
12
+ @model = Example.new
13
+ end
14
+
15
+ should "accept having a scope with the correct signature" do
16
+ assert_accepts have_named_scope("xyz(1)"), @model
17
+ end
18
+
19
+ should "accept having a scope with the correct signature and find options" do
20
+ assert_accepts have_named_scope("xyz(1)").finding(:order => :attr), @model
21
+ end
22
+
23
+ should "reject having a scope with incorrect find options" do
24
+ assert_rejects have_named_scope("xyz(1)").
25
+ finding(:order => 'attr DESC'),
26
+ @model
27
+ end
28
+
29
+ should "reject having a scope with another name" do
30
+ assert_rejects have_named_scope("abc(1)"), @model
31
+ end
32
+
33
+ end
34
+
35
+ should "evaluate the scope in the correct context" do
36
+ define_model :example, :attr => :string do
37
+ named_scope :xyz, lambda {|n|
38
+ { :order => n }
39
+ }
40
+ end
41
+ model = Example.new
42
+ @order = :attr
43
+ assert_accepts have_named_scope("xyz(@order)").
44
+ finding(:order => @order).
45
+ in_context(self),
46
+ model
47
+ end
48
+
49
+ context "a method that does not return a scope" do
50
+ setup do
51
+ klass = Class.new
52
+ klass.class_eval do
53
+ def self.xyz
54
+ 'xyz'
55
+ end
56
+ end
57
+ @model = klass.new
58
+ end
59
+
60
+ should "reject having a named scope with that name" do
61
+ assert_rejects have_named_scope(:xyz), @model
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class HaveReadonlyAttributesMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "an attribute that cannot be set after being saved" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ attr_readonly :attr
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "accept being read-only" do
14
+ assert_accepts have_readonly_attribute(:attr), @model
15
+ end
16
+ end
17
+
18
+ context "an attribute that can be set after being saved" do
19
+ setup do
20
+ define_model :example, :attr => :string
21
+ @model = Example.new
22
+ end
23
+
24
+ should "accept being read-only" do
25
+ assert_rejects have_readonly_attribute(:attr), @model
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,44 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ValidateAcceptanceOfMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "an attribute which must be accepted" do
6
+ setup do
7
+ @model = define_model(:example) do
8
+ validates_acceptance_of :attr
9
+ end.new
10
+ end
11
+
12
+ should "require that attribute to be accepted" do
13
+ assert_accepts validate_acceptance_of(:attr), @model
14
+ end
15
+
16
+ should "not overwrite the default message with nil" do
17
+ assert_accepts validate_acceptance_of(:attr).with_message(nil), @model
18
+ end
19
+ end
20
+
21
+ context "an attribute that does not need to be accepted" do
22
+ setup do
23
+ @model = define_model(:example, :attr => :string).new
24
+ end
25
+
26
+ should "not require that attribute to be accepted" do
27
+ assert_rejects validate_acceptance_of(:attr), @model
28
+ end
29
+ end
30
+
31
+ context "an attribute which must be accepted with a custom message" do
32
+ setup do
33
+ @model = define_model(:example) do
34
+ validates_acceptance_of :attr, :message => 'custom'
35
+ end.new
36
+ end
37
+
38
+ should "require that attribute to be accepted with that message" do
39
+ assert_accepts validate_acceptance_of(:attr).with_message(/custom/),
40
+ @model
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ValidateNumericalityOfMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "a numeric attribute" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ validates_numericality_of :attr
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "only allow numeric values for that attribute" do
14
+ assert_accepts validate_numericality_of(:attr), @model
15
+ end
16
+
17
+ should "not override the default message with a blank" do
18
+ assert_accepts validate_numericality_of(:attr).with_message(nil),
19
+ @model
20
+ end
21
+ end
22
+
23
+ context "a numeric attribute with a custom validation message" do
24
+ setup do
25
+ define_model :example, :attr => :string do
26
+ validates_numericality_of :attr, :message => 'custom'
27
+ end
28
+ @model = Example.new
29
+ end
30
+
31
+ should "only allow numeric values for that attribute with that message" do
32
+ assert_accepts validate_numericality_of(:attr).
33
+ with_message(/custom/),
34
+ @model
35
+ end
36
+
37
+ should "not allow numeric values for that attribute with another message" do
38
+ assert_rejects validate_numericality_of(:attr), @model
39
+ end
40
+ end
41
+
42
+ context "a non-numeric attribute" do
43
+ setup do
44
+ @model = define_model(:example, :attr => :string).new
45
+ end
46
+
47
+ should "not only allow numeric values for that attribute" do
48
+ assert_rejects validate_numericality_of(:attr), @model
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,86 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ValidatePresenceOfMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "a required attribute" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ validates_presence_of :attr
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "require a value" do
14
+ assert_accepts validate_presence_of(:attr), @model
15
+ end
16
+
17
+ should "not override the default message with a blank" do
18
+ assert_accepts validate_presence_of(:attr).with_message(nil), @model
19
+ end
20
+ end
21
+
22
+ context "an optional attribute" do
23
+ setup do
24
+ @model = define_model(:example, :attr => :string).new
25
+ end
26
+
27
+ should "not require a value" do
28
+ assert_rejects validate_presence_of(:attr), @model
29
+ end
30
+ end
31
+
32
+ context "a required has_many association" do
33
+ setup do
34
+ define_model :child
35
+ @model = define_model :parent do
36
+ has_many :children
37
+ validates_presence_of :children
38
+ end.new
39
+ end
40
+
41
+ should "require the attribute to be set" do
42
+ assert_accepts validate_presence_of(:children), @model
43
+ end
44
+ end
45
+
46
+ context "an optional has_many association" do
47
+ setup do
48
+ define_model :child
49
+ @model = define_model :parent do
50
+ has_many :children
51
+ end.new
52
+ end
53
+
54
+ should "not require the attribute to be set" do
55
+ assert_rejects validate_presence_of(:children), @model
56
+ end
57
+ end
58
+
59
+ context "a required has_and_belongs_to_many association" do
60
+ setup do
61
+ define_model :child
62
+ @model = define_model :parent do
63
+ has_and_belongs_to_many :children
64
+ validates_presence_of :children
65
+ end.new
66
+ end
67
+
68
+ should "require the attribute to be set" do
69
+ assert_accepts validate_presence_of(:children), @model
70
+ end
71
+ end
72
+
73
+ context "an optional has_and_belongs_to_many association" do
74
+ setup do
75
+ define_model :child
76
+ @model = define_model :parent do
77
+ has_and_belongs_to_many :children
78
+ end.new
79
+ end
80
+
81
+ should "not require the attribute to be set" do
82
+ assert_rejects validate_presence_of(:children), @model
83
+ end
84
+ end
85
+
86
+ end