shoulda 2.0.6 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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