mcmire-shoulda-matchers 2.5.0 → 2.6.1.docs.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +2 -1
  5. data/Appraisals +62 -22
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +3 -3
  8. data/NEWS.md +87 -4
  9. data/README.md +2 -2
  10. data/Rakefile +18 -0
  11. data/features/activemodel_integration.feature +15 -0
  12. data/features/rails_integration.feature +1 -1
  13. data/features/step_definitions/activemodel_steps.rb +21 -0
  14. data/features/step_definitions/rails_steps.rb +5 -4
  15. data/gemfiles/3.0.gemfile +6 -3
  16. data/gemfiles/3.0.gemfile.lock +14 -4
  17. data/gemfiles/3.1.gemfile +10 -4
  18. data/gemfiles/3.1.gemfile.lock +32 -5
  19. data/gemfiles/3.1_1.9.2.gemfile +21 -0
  20. data/gemfiles/3.1_1.9.2.gemfile.lock +191 -0
  21. data/gemfiles/3.2.gemfile +9 -4
  22. data/gemfiles/3.2.gemfile.lock +28 -5
  23. data/gemfiles/4.0.0.gemfile +11 -3
  24. data/gemfiles/4.0.0.gemfile.lock +42 -5
  25. data/gemfiles/4.0.1.gemfile +11 -3
  26. data/gemfiles/4.0.1.gemfile.lock +42 -5
  27. data/gemfiles/4.1.gemfile +37 -0
  28. data/gemfiles/4.1.gemfile.lock +216 -0
  29. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +202 -0
  30. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +2 -1
  32. data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +165 -0
  33. data/lib/shoulda/matchers/action_controller.rb +2 -0
  34. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +26 -4
  35. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -0
  36. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +2 -0
  37. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
  38. data/lib/shoulda/matchers/active_model/errors.rb +43 -1
  39. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +14 -3
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +100 -45
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +38 -5
  43. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -20
  44. data/lib/shoulda/matchers/active_record/association_matcher.rb +12 -2
  45. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  46. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +24 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  48. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  49. data/lib/shoulda/matchers/active_record.rb +1 -0
  50. data/lib/shoulda/matchers/assertion_error.rb +8 -3
  51. data/lib/shoulda/matchers/doublespeak/double.rb +75 -0
  52. data/lib/shoulda/matchers/doublespeak/double_collection.rb +55 -0
  53. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +28 -0
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +33 -0
  55. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +31 -0
  56. data/lib/shoulda/matchers/doublespeak/structs.rb +10 -0
  57. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/world.rb +39 -0
  59. data/lib/shoulda/matchers/doublespeak.rb +28 -0
  60. data/lib/shoulda/matchers/error.rb +20 -1
  61. data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +35 -0
  62. data/lib/shoulda/matchers/independent/delegate_matcher.rb +293 -0
  63. data/lib/shoulda/matchers/independent.rb +10 -0
  64. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +38 -0
  65. data/lib/shoulda/matchers/integrations/rspec.rb +13 -14
  66. data/lib/shoulda/matchers/integrations/test_unit.rb +19 -15
  67. data/lib/shoulda/matchers/integrations.rb +13 -0
  68. data/lib/shoulda/matchers/rails_shim.rb +16 -0
  69. data/lib/shoulda/matchers/version.rb +1 -1
  70. data/lib/shoulda/matchers.rb +15 -3
  71. data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +82 -0
  72. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +2 -2
  73. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +5 -5
  74. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +4 -4
  75. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +38 -11
  76. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +1 -1
  77. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +1 -1
  78. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +6 -6
  79. data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +314 -0
  80. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +32 -0
  81. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
  82. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
  83. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +38 -0
  84. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +42 -36
  85. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +15 -1
  86. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +4 -0
  87. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +4 -0
  88. data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
  89. data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
  90. data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
  91. data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
  92. data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
  93. data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
  94. data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
  95. data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
  96. data/spec/shoulda/matchers/independent/delegate_matcher/stubbed_target_spec.rb +43 -0
  97. data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +250 -0
  98. data/spec/spec_helper.rb +15 -0
  99. data/spec/support/activemodel_helpers.rb +6 -2
  100. data/spec/support/controller_builder.rb +29 -1
  101. data/spec/support/rails_versions.rb +4 -0
  102. data/spec/support/test_application.rb +1 -1
  103. metadata +59 -10
@@ -236,6 +236,28 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher do
236
236
  end
237
237
  end
238
238
 
239
+ context 'with large numbers' do
240
+ it do
241
+ expect(validating_numericality(greater_than: 100_000))
242
+ .to matcher.is_greater_than(100_000)
243
+ end
244
+
245
+ it do
246
+ expect(validating_numericality(less_than: 100_000))
247
+ .to matcher.is_less_than(100_000)
248
+ end
249
+
250
+ it do
251
+ expect(validating_numericality(greater_than_or_equal_to: 100_000))
252
+ .to matcher.is_greater_than_or_equal_to(100_000)
253
+ end
254
+
255
+ it do
256
+ expect(validating_numericality(less_than_or_equal_to: 100_000))
257
+ .to matcher.is_less_than_or_equal_to(100_000)
258
+ end
259
+ end
260
+
239
261
  context 'with a custom validation message' do
240
262
  it 'accepts when the messages match' do
241
263
  expect(validating_numericality(message: 'custom')).
@@ -144,6 +144,44 @@ describe Shoulda::Matchers::ActiveModel::ValidatePresenceOfMatcher do
144
144
  end
145
145
  end
146
146
 
147
+ if rails_4_x?
148
+ context 'against a pre-set password in a model that has_secure_password' do
149
+ it 'raises a CouldNotSetPasswordError exception' do
150
+ user_class = define_model :user, password_digest: :string do
151
+ has_secure_password validations: false
152
+ validates_presence_of :password
153
+ end
154
+
155
+ user = user_class.new
156
+ user.password = 'something'
157
+
158
+ error_class = Shoulda::Matchers::ActiveModel::CouldNotSetPasswordError
159
+ expect do
160
+ expect(user).to validate_presence_of(:password)
161
+ end.to raise_error(error_class)
162
+ end
163
+ end
164
+ end
165
+
166
+ context 'when the attribute being tested intercepts the blank value we set on it (issue #479)' do
167
+ context 'for a non-collection attribute' do
168
+ it 'does not raise an error' do
169
+ record = define_model :example, attr: :string do
170
+ validates :attr, presence: true
171
+
172
+ def attr=(value)
173
+ value = '' if value.nil?
174
+ super(value)
175
+ end
176
+ end.new
177
+
178
+ expect do
179
+ expect(record).to validate_presence_of(:attr)
180
+ end.not_to raise_error
181
+ end
182
+ end
183
+ end
184
+
147
185
  def matcher
148
186
  validate_presence_of(:attr)
149
187
  end
@@ -122,6 +122,40 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
122
122
  not_to matcher.scoped_to(:fake)
123
123
  end
124
124
 
125
+ if rails_gte_4_1?
126
+ context 'when the scoped attribute is an enum' do
127
+ it 'accepts' do
128
+ expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: 0)).
129
+ to matcher.scoped_to(:scope1)
130
+ end
131
+
132
+ context 'with a nil value' do
133
+ it 'accepts' do
134
+ expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: nil)).
135
+ to matcher.scoped_to(:scope1)
136
+ end
137
+ end
138
+
139
+ context 'when too narrow of a scope is specified' do
140
+ it 'rejects' do
141
+ expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
142
+ not_to matcher.scoped_to(:scope1, :scope2, :other)
143
+ end
144
+ end
145
+
146
+ context 'when too broad of a scope is specified' do
147
+ it 'rejects' do
148
+ expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
149
+ not_to matcher.scoped_to(:scope1)
150
+ end
151
+ end
152
+
153
+ def validating_scoped_uniqueness_with_enum_with_two_scopes
154
+ validating_scoped_uniqueness_with_enum([:scope1, :scope2], scope1: 0, scope2: 0)
155
+ end
156
+ end
157
+ end
158
+
125
159
  context 'when the scoped attribute is a date' do
126
160
  it "accepts" do
127
161
  expect(validating_scoped_uniqueness([:scope1], :date, scope1: Date.today)).
@@ -250,6 +284,14 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
250
284
  model
251
285
  end
252
286
 
287
+ def validating_scoped_uniqueness_with_enum(*args)
288
+ attributes = args.extract_options!
289
+ model = define_scoped_model(*args)
290
+ model.enum scope1: [:foo, :bar]
291
+ create_existing_record(attributes)
292
+ model.new
293
+ end
294
+
253
295
  def validating_scoped_uniqueness_with_conflicting_next(*args)
254
296
  attributes = args.extract_options!
255
297
  model = define_scoped_model(*args).new
@@ -341,42 +383,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
341
383
  end
342
384
  end
343
385
 
344
- context "a model with non-nullable attribute" do
345
- context "of type" do
346
- [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |type|
347
- context type do
348
- it "does not raise an error" do
349
- model = define_model_with_non_nullable(type)
350
- expect { expect(model).to matcher }.not_to raise_error
351
- end
352
- end
353
- end
354
- end
355
-
356
- context "that is a primary key" do
357
- it "does not cause duplicate entry errors by re-using default values for primary keys" do
358
- create_table :examples, id: false do |t|
359
- t.string :attr
360
- t.integer :non_nullable, primary: true
361
- end
362
- model_class = define_model(:example, attr: :string) do
363
- validates_uniqueness_of :attr
364
- end
365
- model_1 = model_class.new
366
- model_2 = model_class.new
367
- expect(model_1).to matcher
368
- expect { expect(model_2).to matcher }.not_to raise_error
369
- end
370
- end
371
-
372
- def define_model_with_non_nullable(type)
373
- define_model(:example, attr: :string, non_nullable: { type: type, options: { null: false } }) do
374
- attr_accessible :attr, :non_nullable
375
- validates_uniqueness_of :attr
376
- end.new
377
- end
378
- end
379
-
380
386
  def case_sensitive_validation_with_existing_value(attr_type)
381
387
  model = define_model(:example, attr: attr_type) do
382
388
  attr_accessible :attr
@@ -30,7 +30,6 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
30
30
  end
31
31
 
32
32
  it 'accepts a polymorphic association' do
33
- define_model :parent
34
33
  define_model :child, parent_type: :string, parent_id: :integer do
35
34
  belongs_to :parent, polymorphic: true
36
35
  end
@@ -66,6 +65,21 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
66
65
  expect(belonging_to_parent).not_to belong_to(:parent).counter_cache
67
66
  end
68
67
 
68
+ it 'accepts an association with a valid :inverse_of option' do
69
+ expect(belonging_to_parent(inverse_of: :children))
70
+ .to belong_to(:parent).inverse_of(:children)
71
+ end
72
+
73
+ it 'rejects an association with a bad :inverse_of option' do
74
+ expect(belonging_to_parent(inverse_of: :other_children))
75
+ .not_to belong_to(:parent).inverse_of(:children)
76
+ end
77
+
78
+ it 'rejects an association that has no :inverse_of option' do
79
+ expect(belonging_to_parent)
80
+ .not_to belong_to(:parent).inverse_of(:children)
81
+ end
82
+
69
83
  it 'accepts an association with a valid :conditions option' do
70
84
  define_model :parent, adopter: :boolean
71
85
  define_model(:child, parent_id: :integer).tap do |model|
@@ -57,6 +57,10 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatchers::ModelReflection d
57
57
  describe '#join_table' do
58
58
  context 'when the association was defined with a :join_table option' do
59
59
  it 'returns the value of the option' do
60
+ create_table :foos, id: false do |t|
61
+ t.integer :person_id
62
+ t.integer :country_id
63
+ end
60
64
  define_model(:person, country_id: :integer)
61
65
  country_model = define_model(:country) do
62
66
  has_and_belongs_to_many :people, join_table: 'foos'
@@ -52,6 +52,10 @@ describe Shoulda::Matchers::ActiveRecord::HaveDbIndexMatcher do
52
52
  expect(have_db_index(:user_id).unique(true).description).to match(/a unique index/)
53
53
  end
54
54
 
55
+ it 'describes a unique index as unique when no argument is given' do
56
+ expect(have_db_index(:user_id).unique.description).to match(/a unique index/)
57
+ end
58
+
55
59
  it 'describes a non-unique index as non-unique' do
56
60
  expect(have_db_index(:user_id).unique(false).description).to match(/a non-unique index/)
57
61
  end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ module Shoulda::Matchers::Doublespeak
4
+ describe DoubleCollection do
5
+ describe '#register_stub' do
6
+ it 'calls DoubleImplementationRegistry.find correctly' do
7
+ double_collection = described_class.new(:klass)
8
+ DoubleImplementationRegistry.expects(:find).with(:stub)
9
+ double_collection.register_stub(:a_method)
10
+ end
11
+
12
+ it 'calls Double.new correctly' do
13
+ DoubleImplementationRegistry.stubs(:find).returns(:implementation)
14
+ double_collection = described_class.new(:klass)
15
+ Double.expects(:new).with(:klass, :a_method, :implementation)
16
+ double_collection.register_stub(:a_method)
17
+ end
18
+ end
19
+
20
+ describe '#register_proxy' do
21
+ it 'calls DoubleImplementationRegistry.find correctly' do
22
+ double_collection = described_class.new(:klass)
23
+ DoubleImplementationRegistry.expects(:find).with(:proxy)
24
+ double_collection.register_proxy(:a_method)
25
+ end
26
+
27
+ it 'calls Double.new correctly' do
28
+ DoubleImplementationRegistry.stubs(:find).returns(:implementation)
29
+ double_collection = described_class.new(:klass)
30
+ Double.expects(:new).with(:klass, :a_method, :implementation)
31
+ double_collection.register_proxy(:a_method)
32
+ end
33
+ end
34
+
35
+ describe '#activate' do
36
+ it 'replaces all registered methods with doubles' do
37
+ klass = create_class(first_method: 1, second_method: 2)
38
+ double_collection = described_class.new(klass)
39
+ double_collection.register_stub(:first_method)
40
+ double_collection.register_stub(:second_method)
41
+
42
+ double_collection.activate
43
+
44
+ instance = klass.new
45
+ expect(instance.first_method).to eq nil
46
+ expect(instance.second_method).to eq nil
47
+ end
48
+ end
49
+
50
+ describe '#deactivate' do
51
+ it 'restores the original methods that were doubled' do
52
+ klass = create_class(first_method: 1, second_method: 2)
53
+ double_collection = described_class.new(klass)
54
+ double_collection.register_stub(:first_method)
55
+ double_collection.register_stub(:second_method)
56
+
57
+ double_collection.activate
58
+ double_collection.deactivate
59
+
60
+ instance = klass.new
61
+ expect(instance.first_method).to eq 1
62
+ expect(instance.second_method).to eq 2
63
+ end
64
+ end
65
+
66
+ describe '#calls_to' do
67
+ it 'returns all calls to the given method' do
68
+ klass = create_class(a_method: nil)
69
+ double_collection = described_class.new(klass)
70
+ double_collection.register_stub(:a_method)
71
+ double_collection.activate
72
+
73
+ actual_calls = [
74
+ { args: [:some, :args, :here] },
75
+ { args: [:some, :args], block: -> { :whatever } }
76
+ ]
77
+ instance = klass.new
78
+ instance.a_method(*actual_calls[0][:args])
79
+ instance.a_method(*actual_calls[1][:args], &actual_calls[1][:block])
80
+
81
+ calls = double_collection.calls_to(:a_method)
82
+ expect(calls[0].args).to eq actual_calls[0][:args]
83
+ expect(calls[1].args).to eq actual_calls[1][:args]
84
+ expect(calls[1].block).to eq actual_calls[1][:block]
85
+ end
86
+
87
+ it 'returns an empty array if the method has never been doubled' do
88
+ klass = create_class
89
+ double_collection = described_class.new(klass)
90
+ expect(double_collection.calls_to(:non_existent_method)).to eq []
91
+ end
92
+ end
93
+
94
+ def create_class(methods = {})
95
+ Class.new.tap do |klass|
96
+ methods.each do |name, value|
97
+ klass.__send__(:define_method, name) { |*args| value }
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Shoulda::Matchers::Doublespeak
4
+ describe DoubleImplementationRegistry do
5
+ describe '.find' do
6
+ it 'returns an instance of StubImplementation if given :stub' do
7
+ expect(described_class.find(:stub)).to be_a(StubImplementation)
8
+ end
9
+
10
+ it 'returns ProxyImplementation if given :proxy' do
11
+ expect(described_class.find(:proxy)).to be_a(ProxyImplementation)
12
+ end
13
+
14
+ it 'raises an ArgumentError if not given a registered implementation' do
15
+ expect {
16
+ expect(described_class.find(:something_else))
17
+ }.to raise_error(ArgumentError)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ module Shoulda::Matchers::Doublespeak
4
+ describe Double do
5
+ describe '#to_return' do
6
+ it 'tells its implementation to call the given block' do
7
+ sent_block = -> { }
8
+ actual_block = nil
9
+ implementation = stub
10
+ implementation.singleton_class.__send__(:define_method, :returns) do |&block|
11
+ actual_block = block
12
+ end
13
+ double = described_class.new(:klass, :a_method, implementation)
14
+ double.to_return(&sent_block)
15
+ expect(actual_block).to eq sent_block
16
+ end
17
+
18
+ it 'tells its implementation to return the given value' do
19
+ implementation = mock()
20
+ implementation.expects(:returns).with(:implementation)
21
+ double = described_class.new(:klass, :a_method, implementation)
22
+ double.to_return(:implementation)
23
+ end
24
+
25
+ it 'prefers a block over a non-block' do
26
+ sent_block = -> { }
27
+ actual_block = nil
28
+ implementation = stub
29
+ implementation.singleton_class.__send__(:define_method, :returns) do |&block|
30
+ actual_block = block
31
+ end
32
+ double = described_class.new(:klass, :a_method, implementation)
33
+ double.to_return(:value, &sent_block)
34
+ expect(actual_block).to eq sent_block
35
+ end
36
+ end
37
+
38
+ describe '#activate' do
39
+ it 'replaces the method with an implementation' do
40
+ implementation = stub
41
+ klass = create_class(a_method: 42)
42
+ instance = klass.new
43
+ double = described_class.new(klass, :a_method, implementation)
44
+ args = [:any, :args]
45
+ block = -> {}
46
+ implementation.expects(:call).with(double, instance, args, block)
47
+
48
+ double.activate
49
+ instance.a_method(*args, &block)
50
+ end
51
+ end
52
+
53
+ describe '#deactivate' do
54
+ it 'restores the original method after being doubled' do
55
+ implementation = stub(call: nil)
56
+ klass = create_class(a_method: 42)
57
+ instance = klass.new
58
+ double = described_class.new(klass, :a_method, implementation)
59
+
60
+ double.activate
61
+ double.deactivate
62
+ expect(instance.a_method).to eq 42
63
+ end
64
+
65
+ it 'still restores the original method if #activate was called twice' do
66
+ implementation = stub(call: nil)
67
+ klass = create_class(a_method: 42)
68
+ instance = klass.new
69
+ double = described_class.new(klass, :a_method, implementation)
70
+
71
+ double.activate
72
+ double.activate
73
+ double.deactivate
74
+ expect(instance.a_method).to eq 42
75
+ end
76
+
77
+ it 'does nothing if the method has not been doubled' do
78
+ implementation = stub(call: nil)
79
+ klass = create_class(a_method: 42)
80
+ instance = klass.new
81
+ double = described_class.new(klass, :a_method, implementation)
82
+
83
+ double.deactivate
84
+ expect(instance.a_method).to eq 42
85
+ end
86
+ end
87
+
88
+ describe '#record_call' do
89
+ it 'stores the arguments and block given to the method in calls' do
90
+ double = described_class.new(:klass, :a_method, :implementation)
91
+ calls = [
92
+ [:any, :args], :block,
93
+ [:more, :args]
94
+ ]
95
+ double.record_call(calls[0][0], calls[0][1])
96
+ double.record_call(calls[1][0], nil)
97
+
98
+ expect(double.calls[0].args).to eq calls[0][0]
99
+ expect(double.calls[0].block).to eq calls[0][1]
100
+ expect(double.calls[1].args).to eq calls[1][0]
101
+ end
102
+ end
103
+
104
+ describe '#call_original_method' do
105
+ it 'binds the stored method object to the class and calls it with the given args and block' do
106
+ klass = create_class(a_method: nil)
107
+ instance = klass.new
108
+ actual_args = actual_block = method_called = nil
109
+ expected_args = [:one, :two, :three]
110
+ expected_block = -> { }
111
+ double = described_class.new(klass, :a_method, :implementation)
112
+
113
+ klass.__send__(:define_method, :a_method) do |*args, &block|
114
+ actual_args = expected_args
115
+ actual_block = expected_block
116
+ method_called = true
117
+ end
118
+
119
+ double.activate
120
+ double.call_original_method(instance, expected_args, expected_block)
121
+
122
+ expect(expected_args).to eq actual_args
123
+ expect(expected_block).to eq actual_block
124
+ expect(method_called).to eq true
125
+ end
126
+
127
+ it 'does nothing if no method has been stored' do
128
+ double = described_class.new(:klass, :a_method, :implementation)
129
+
130
+ expect {
131
+ double.call_original_method(:instance, [:any, :args], nil)
132
+ }.not_to raise_error
133
+ end
134
+ end
135
+
136
+ def create_class(methods = {})
137
+ Class.new.tap do |klass|
138
+ methods.each do |name, value|
139
+ klass.__send__(:define_method, name) { |*args| value }
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ module Shoulda::Matchers::Doublespeak
4
+ describe ObjectDouble do
5
+ it 'responds to any method' do
6
+ double = described_class.new
7
+
8
+ expect(double.respond_to?(:foo)).to be_true
9
+ expect(double.respond_to?(:bar)).to be_true
10
+ expect(double.respond_to?(:baz)).to be_true
11
+ end
12
+
13
+ it 'returns nil from any method call' do
14
+ double = described_class.new
15
+
16
+ expect(double.foo).to be_nil
17
+ expect(double.bar).to be_nil
18
+ expect(double.baz).to be_nil
19
+ end
20
+
21
+ it 'records every method call' do
22
+ double = described_class.new
23
+
24
+ block = -> { :some_return_value }
25
+ double.foo
26
+ double.bar(42)
27
+ double.baz(:zing, :zang, &block)
28
+
29
+ expect(double.calls.size).to eq 3
30
+ double.calls[0].tap do |call|
31
+ expect(call.args).to eq []
32
+ expect(call.block).to eq nil
33
+ end
34
+ double.calls[1].tap do |call|
35
+ expect(call.args).to eq [42]
36
+ expect(call.block).to eq nil
37
+ end
38
+ double.calls[2].tap do |call|
39
+ expect(call.args).to eq [:zing, :zang]
40
+ expect(call.block).to eq block
41
+ end
42
+ end
43
+
44
+ describe '#calls_to' do
45
+ it 'returns all of the invocations of the given method and their arguments/block' do
46
+ double = described_class.new
47
+
48
+ block = -> { :some_return_value }
49
+ double.foo
50
+ double.foo(42)
51
+ double.foo(:zing, :zang, &block)
52
+ double.some_other_method(:doesnt_matter)
53
+
54
+ calls = double.calls_to(:foo)
55
+
56
+ expect(calls.size).to eq 3
57
+ calls[0].tap do |call|
58
+ expect(call.args).to eq []
59
+ expect(call.block).to eq nil
60
+ end
61
+ calls[1].tap do |call|
62
+ expect(call.args).to eq [42]
63
+ expect(call.block).to eq nil
64
+ end
65
+ calls[2].tap do |call|
66
+ expect(call.args).to eq [:zing, :zang]
67
+ expect(call.block).to eq block
68
+ end
69
+ end
70
+
71
+ it 'returns an empty array if the given method was never called' do
72
+ double = described_class.new
73
+ expect(double.calls_to(:unknown_method)).to eq []
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module Shoulda::Matchers::Doublespeak
4
+ describe ProxyImplementation do
5
+ describe '#returns' do
6
+ it 'delegates to its stub_implementation' do
7
+ stub_implementation = build_stub_implementation
8
+ stub_implementation.expects(:returns).with(:value)
9
+ implementation = described_class.new(stub_implementation)
10
+ implementation.returns(:value)
11
+ end
12
+ end
13
+
14
+ describe '#call' do
15
+ it 'delegates to its stub_implementation' do
16
+ stub_implementation = build_stub_implementation
17
+ double = build_double
18
+ stub_implementation.expects(:call).with(double, :object, :args, :block)
19
+ implementation = described_class.new(stub_implementation)
20
+ implementation.call(double, :object, :args, :block)
21
+ end
22
+
23
+ it 'calls #call_original_method on the double' do
24
+ stub_implementation = build_stub_implementation
25
+ implementation = described_class.new(stub_implementation)
26
+ double = build_double
27
+ double.expects(:call_original_method).with(:object, :args, :block)
28
+ implementation.call(double, :object, :args, :block)
29
+ end
30
+ end
31
+
32
+ def build_stub_implementation
33
+ stub(returns: nil, call: nil)
34
+ end
35
+
36
+ def build_double
37
+ stub(call_original_method: nil)
38
+ end
39
+ end
40
+ end