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,141 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ValidateUniquenessOfMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "a unique attribute" do
6
+ setup do
7
+ @model = define_model(:example, :attr => :string,
8
+ :other => :integer) do
9
+ validates_uniqueness_of :attr
10
+ end.new
11
+ end
12
+
13
+ context "with an existing value" do
14
+ setup do
15
+ @existing = Example.create!(:attr => 'value', :other => 1)
16
+ end
17
+
18
+ should "require a unique value for that attribute" do
19
+ assert_accepts validate_uniqueness_of(:attr), @model
20
+ end
21
+
22
+ should "pass when the subject is an existing record" do
23
+ assert_accepts validate_uniqueness_of(:attr), @existing
24
+ end
25
+
26
+ should "fail when a scope is specified" do
27
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:other), @model
28
+ end
29
+ end
30
+
31
+ context "without an existing value" do
32
+ setup do
33
+ assert_nil Example.find(:first)
34
+ end
35
+
36
+ should "fail to require a unique value" do
37
+ assert_rejects validate_uniqueness_of(:attr), @model
38
+ end
39
+ end
40
+ end
41
+
42
+ context "a unique attribute with a custom error and an existing value" do
43
+ setup do
44
+ @model = define_model(:example, :attr => :string) do
45
+ validates_uniqueness_of :attr, :message => 'Bad value'
46
+ end.new
47
+ Example.create!
48
+ end
49
+
50
+ should "fail when checking the default message" do
51
+ assert_rejects validate_uniqueness_of(:attr), @model
52
+ end
53
+
54
+ should "fail when checking a message that doesn't match" do
55
+ assert_rejects validate_uniqueness_of(:attr).with_message(/abc/i), @model
56
+ end
57
+
58
+ should "pass when checking a message that matches" do
59
+ assert_accepts validate_uniqueness_of(:attr).with_message(/bad/i), @model
60
+ end
61
+ end
62
+
63
+ context "a scoped unique attribute with an existing value" do
64
+ setup do
65
+ @model = define_model(:example, :attr => :string,
66
+ :scope1 => :integer,
67
+ :scope2 => :integer) do
68
+ validates_uniqueness_of :attr, :scope => [:scope1, :scope2]
69
+ end.new
70
+ @existing = Example.create!(:attr => 'value', :scope1 => 1, :scope2 => 2)
71
+ end
72
+
73
+ should "pass when the correct scope is specified" do
74
+ assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
75
+ @model
76
+ end
77
+
78
+ should "pass when the subject is an existing record" do
79
+ assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
80
+ @existing
81
+ end
82
+
83
+ should "fail when a different scope is specified" do
84
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:scope1), @model
85
+ end
86
+
87
+ should "fail when no scope is specified" do
88
+ assert_rejects validate_uniqueness_of(:attr), @model
89
+ end
90
+
91
+ should "fail when a non-existent attribute is specified as a scope" do
92
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:fake), @model
93
+ end
94
+ end
95
+
96
+ context "a non-unique attribute with an existing value" do
97
+ setup do
98
+ @model = define_model(:example, :attr => :string).new
99
+ Example.create!(:attr => 'value')
100
+ end
101
+
102
+ should "not require a unique value for that attribute" do
103
+ assert_rejects validate_uniqueness_of(:attr), @model
104
+ end
105
+ end
106
+
107
+ context "a case sensitive unique attribute with an existing value" do
108
+ setup do
109
+ @model = define_model(:example, :attr => :string) do
110
+ validates_uniqueness_of :attr, :case_sensitive => true
111
+ end.new
112
+ Example.create!(:attr => 'value')
113
+ end
114
+
115
+ should "not require a unique, case-insensitive value for that attribute" do
116
+ assert_rejects validate_uniqueness_of(:attr).case_insensitive, @model
117
+ end
118
+
119
+ should "require a unique, case-sensitive value for that attribute" do
120
+ assert_accepts validate_uniqueness_of(:attr), @model
121
+ end
122
+ end
123
+
124
+ context "a case sensitive unique integer attribute with an existing value" do
125
+ setup do
126
+ @model = define_model(:example, :attr => :integer) do
127
+ validates_uniqueness_of :attr, :case_sensitive => true
128
+ end.new
129
+ Example.create!(:attr => 'value')
130
+ end
131
+
132
+ should "require a unique, case-insensitive value for that attribute" do
133
+ assert_accepts validate_uniqueness_of(:attr).case_insensitive, @model
134
+ end
135
+
136
+ should "require a unique, case-sensitive value for that attribute" do
137
+ assert_accepts validate_uniqueness_of(:attr), @model
138
+ end
139
+ end
140
+
141
+ end
@@ -0,0 +1,61 @@
1
+ class Test::Unit::TestCase
2
+ def create_table(table_name, &block)
3
+ connection = ActiveRecord::Base.connection
4
+
5
+ begin
6
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
7
+ connection.create_table(table_name, &block)
8
+ @created_tables ||= []
9
+ @created_tables << table_name
10
+ connection
11
+ rescue Exception => e
12
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
13
+ raise e
14
+ end
15
+ end
16
+
17
+ def define_model_class(class_name, &block)
18
+ klass = Class.new(ActiveRecord::Base)
19
+ Object.const_set(class_name, klass)
20
+
21
+ klass.class_eval(&block) if block_given?
22
+
23
+ @defined_constants ||= []
24
+ @defined_constants << class_name
25
+
26
+ klass
27
+ end
28
+
29
+ def define_model(name, columns = {}, &block)
30
+ class_name = name.to_s.pluralize.classify
31
+ table_name = class_name.tableize
32
+
33
+ create_table(table_name) do |table|
34
+ columns.each do |name, type|
35
+ table.column name, type
36
+ end
37
+ end
38
+
39
+ define_model_class(class_name, &block)
40
+ end
41
+
42
+ def teardown_with_models
43
+ if @defined_constants
44
+ @defined_constants.each do |class_name|
45
+ Object.send(:remove_const, class_name)
46
+ end
47
+ end
48
+
49
+ if @created_tables
50
+ @created_tables.each do |table_name|
51
+ ActiveRecord::Base.
52
+ connection.
53
+ execute("DROP TABLE IF EXISTS #{table_name}")
54
+ end
55
+ end
56
+
57
+ teardown_without_models
58
+ end
59
+ alias_method :teardown_without_models, :teardown
60
+ alias_method :teardown, :teardown_with_models
61
+ end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class AutoloadMacroTest < Test::Unit::TestCase # :nodoc:
4
+ context "The macro auto-loader" do
5
+ should "load macros from the plugins" do
6
+ assert self.class.respond_to?('plugin_macro')
7
+ end
8
+
9
+ should "load macros from the gems" do
10
+ assert self.class.respond_to?('gem_macro')
11
+ end
12
+
13
+ should "load custom macros from ROOT/test/shoulda_macros" do
14
+ assert self.class.respond_to?('custom_macro')
15
+ end
16
+ end
17
+ end
18
+
@@ -180,4 +180,62 @@ class HelpersTest < Test::Unit::TestCase # :nodoc:
180
180
  end
181
181
  end
182
182
  end
183
+
184
+ context "a matching matcher" do
185
+ setup do
186
+ @matcher = stub('matcher', :matches? => true,
187
+ :failure_message => 'bad failure message',
188
+ :negative_failure_message => 'big time failure')
189
+ end
190
+
191
+ should "pass when given to assert_accepts" do
192
+ assert_accepts @matcher, 'target'
193
+ end
194
+
195
+ context "when given to assert_rejects" do
196
+ setup do
197
+ begin
198
+ assert_rejects @matcher, 'target'
199
+ rescue Test::Unit::AssertionFailedError => @error
200
+ end
201
+ end
202
+
203
+ should "fail" do
204
+ assert_not_nil @error
205
+ end
206
+
207
+ should "use the error message from the matcher" do
208
+ assert_equal 'big time failure', @error.message
209
+ end
210
+ end
211
+ end
212
+
213
+ context "a non-matching matcher" do
214
+ setup do
215
+ @matcher = stub('matcher', :matches? => false,
216
+ :failure_message => 'big time failure',
217
+ :negative_failure_message => 'bad failure message')
218
+ end
219
+
220
+ should "pass when given to assert_rejects" do
221
+ assert_rejects @matcher, 'target'
222
+ end
223
+
224
+ context "when given to assert_accepts" do
225
+ setup do
226
+ begin
227
+ assert_accepts @matcher, 'target'
228
+ rescue Test::Unit::AssertionFailedError => @error
229
+ end
230
+ end
231
+
232
+ should "fail" do
233
+ assert_not_nil @error
234
+ end
235
+
236
+ should "use the error message from the matcher" do
237
+ assert_equal 'big time failure', @error.message
238
+ end
239
+ end
240
+ end
183
241
  end
@@ -1,7 +1,7 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
2
 
3
3
  class PrivateHelpersTest < Test::Unit::TestCase # :nodoc:
4
- include ThoughtBot::Shoulda::Private
4
+ include Shoulda::Private
5
5
  context "get_options!" do
6
6
  should "remove opts from args" do
7
7
  args = [:a, :b, {}]
@@ -119,21 +119,21 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
119
119
 
120
120
  def test_should_create_a_new_context
121
121
  assert_nothing_raised do
122
- Thoughtbot::Shoulda::Context.new("context name", self) do; end
122
+ Shoulda::Context.new("context name", self) do; end
123
123
  end
124
124
  end
125
125
 
126
126
  def test_should_create_a_nested_context
127
127
  assert_nothing_raised do
128
- parent = Thoughtbot::Shoulda::Context.new("Parent", self) do; end
129
- child = Thoughtbot::Shoulda::Context.new("Child", parent) do; end
128
+ parent = Shoulda::Context.new("Parent", self) do; end
129
+ child = Shoulda::Context.new("Child", parent) do; end
130
130
  end
131
131
  end
132
132
 
133
133
  def test_should_name_a_contexts_correctly
134
- parent = Thoughtbot::Shoulda::Context.new("Parent", self) do; end
135
- child = Thoughtbot::Shoulda::Context.new("Child", parent) do; end
136
- grandchild = Thoughtbot::Shoulda::Context.new("GrandChild", child) do; end
134
+ parent = Shoulda::Context.new("Parent", self) do; end
135
+ child = Shoulda::Context.new("Child", parent) do; end
136
+ grandchild = Shoulda::Context.new("GrandChild", child) do; end
137
137
 
138
138
  assert_equal "Parent", parent.full_name
139
139
  assert_equal "Parent Child", child.full_name
@@ -143,7 +143,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
143
143
  # Should statements
144
144
 
145
145
  def test_should_have_should_hashes_when_given_should_statements
146
- context = Thoughtbot::Shoulda::Context.new("name", self) do
146
+ context = Shoulda::Context.new("name", self) do
147
147
  should "be good" do; end
148
148
  should "another" do; end
149
149
  end
@@ -155,7 +155,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
155
155
  # setup and teardown
156
156
 
157
157
  def test_should_capture_setup_and_teardown_blocks
158
- context = Thoughtbot::Shoulda::Context.new("name", self) do
158
+ context = Shoulda::Context.new("name", self) do
159
159
  setup do; "setup"; end
160
160
  teardown do; "teardown"; end
161
161
  end
@@ -167,7 +167,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
167
167
  # building
168
168
 
169
169
  def test_should_create_shoulda_test_for_each_should_on_build
170
- context = Thoughtbot::Shoulda::Context.new("name", self) do
170
+ context = Shoulda::Context.new("name", self) do
171
171
  should "one" do; end
172
172
  should "two" do; end
173
173
  end
@@ -178,7 +178,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
178
178
 
179
179
  def test_should_create_test_methods_on_build
180
180
  tu_class = Test::Unit::TestCase
181
- context = Thoughtbot::Shoulda::Context.new("A Context", tu_class) do
181
+ context = Shoulda::Context.new("A Context", tu_class) do
182
182
  should "define the test" do; end
183
183
  end
184
184
 
@@ -188,7 +188,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
188
188
 
189
189
  def test_should_create_test_methods_on_build_when_subcontext
190
190
  tu_class = Test::Unit::TestCase
191
- context = Thoughtbot::Shoulda::Context.new("A Context", tu_class) do
191
+ context = Shoulda::Context.new("A Context", tu_class) do
192
192
  context "with a child" do
193
193
  should "define the test" do; end
194
194
  end
@@ -203,21 +203,21 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
203
203
  def test_should_create_a_new_context_and_build_it_on_Test_Unit_context
204
204
  c = mock("context")
205
205
  c.expects(:build)
206
- Thoughtbot::Shoulda::Context.expects(:new).with("foo", kind_of(Class)).returns(c)
206
+ Shoulda::Context.expects(:new).with("foo", kind_of(Class)).returns(c)
207
207
  self.class.context "foo" do; end
208
208
  end
209
209
 
210
210
  def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should
211
211
  s = mock("test")
212
- Thoughtbot::Shoulda::Context.any_instance.expects(:should).with("rock", {}).returns(s)
213
- Thoughtbot::Shoulda::Context.any_instance.expects(:build)
212
+ Shoulda::Context.any_instance.expects(:should).with("rock", {}).returns(s)
213
+ Shoulda::Context.any_instance.expects(:build)
214
214
  self.class.should "rock" do; end
215
215
  end
216
216
 
217
217
  def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should_eventually
218
218
  s = mock("test")
219
- Thoughtbot::Shoulda::Context.any_instance.expects(:should_eventually).with("rock").returns(s)
220
- Thoughtbot::Shoulda::Context.any_instance.expects(:build)
219
+ Shoulda::Context.any_instance.expects(:should_eventually).with("rock").returns(s)
220
+ Shoulda::Context.any_instance.expects(:build)
221
221
  self.class.should_eventually "rock" do; end
222
222
  end
223
223
 
@@ -1,7 +1,7 @@
1
1
  class PostsController < ApplicationController
2
2
  before_filter :ensure_logged_in
3
3
  before_filter :load_user
4
-
4
+
5
5
  def index
6
6
  @posts = @user.posts
7
7
 
@@ -19,6 +19,7 @@ class PostsController < ApplicationController
19
19
 
20
20
  def show
21
21
  @post = @user.posts.find(params[:id])
22
+ @false_flag = false
22
23
 
23
24
  respond_to do |format|
24
25
  format.html { render :layout => 'wide' }
@@ -68,17 +69,17 @@ class PostsController < ApplicationController
68
69
  def destroy
69
70
  @post = @user.posts.find(params[:id])
70
71
  @post.destroy
71
-
72
+
72
73
  flash[:notice] = "Post was removed"
73
-
74
+
74
75
  respond_to do |format|
75
76
  format.html { redirect_to user_posts_url(@post.user) }
76
77
  format.xml { head :ok }
77
78
  end
78
79
  end
79
-
80
+
80
81
  private
81
-
82
+
82
83
  def load_user
83
84
  @user = User.find(params[:user_id])
84
85
  end
@@ -0,0 +1,10 @@
1
+ module Pets
2
+ class Dog < ActiveRecord::Base
3
+ belongs_to :user, :foreign_key => :owner_id
4
+ belongs_to :address, :dependent => :destroy
5
+ has_many :treats
6
+ has_and_belongs_to_many :fleas, :join_table => :fleas
7
+ validates_presence_of :treats, :fleas
8
+ validates_presence_of :owner_id
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ class Treat < ActiveRecord::Base
2
+
3
+ end