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,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