Skip to content

Commit fdcc72d

Browse files
committed
Fix === for multiple mocks of the same model
When the same model is mocked more than once in the same context, comparing with the model class using `#===` would only return true for the first of the mocks. All following mocks would return false when compared to the original model class using `#===`. For `#===` to work correctly, we register each mock in a "mock_store" on the `RSpec::ActiveModel::Mocks::Mocks` module. The store is implemented as an RSpec stub, so it is reset after each spec example. Fixes #60
1 parent b6d57c8 commit fdcc72d

File tree

4 files changed

+36
-1
lines changed

4 files changed

+36
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
### Development
22
[Full Changelog](https://github.com/rspec/rspec-activemodel-mocks/compare/v1.2.0...main)
33

4+
Bug fixes:
5+
6+
* Fix `===` to work with multiple mocks. (@bquorning, #61)
7+
48
### 1.2.0 / 2023-12-10
59
[Full Changelog](https://github.com/rspec/rspec-activemodel-mocks/compare/v1.1.0...v1.2.0)
610

lib/rspec/active_model/mocks/mocks.rb

+6-1
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,16 @@ def self.param_delimiter; "-"; end
135135
:blank? => false }.merge(stubs)
136136

137137
double("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
138+
mock_store_klass = RSpec::ActiveModel::Mocks::Mocks
138139
if model_class.method(:===).owner == Module && !stubs.key?(:===)
140+
allow(mock_store_klass).to receive(:mock_store) do
141+
@mock_store ||= Hash.new { |h, k| h[k] = [] }
142+
end
139143
allow(model_class).to receive(:===).and_wrap_original do |original, other|
140-
m === other || original.call(other)
144+
mock_store_klass.mock_store[model_class].any? { |ms| ms === other } || original.call(other)
141145
end
142146
end
147+
mock_store_klass.mock_store[model_class] << m if mock_store_klass.respond_to?(:mock_store)
143148
msingleton = class << m; self; end
144149
msingleton.class_eval do
145150
include ActiveModelInstanceMethods

spec/rspec/active_model/mocks/mock_model_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,19 @@ def self.===(_other)
209209
end
210210
# rubocop:enable Lint/LiteralAsCondition
211211
end
212+
213+
it "works for multiple mocks of the same model" do
214+
foo = mock_model(MockableModel)
215+
bar = mock_model(MockableModel)
216+
baz = mock_model(MockableModelNoPrimaryKey)
217+
quz = mock_model(MockableModel)
218+
219+
expect(MockableModel === foo).to be(true)
220+
expect(MockableModel === bar).to be(true)
221+
expect(MockableModel === baz).to be(false)
222+
expect(MockableModelNoPrimaryKey === baz).to be(true)
223+
expect(MockableModel === quz).to be(true)
224+
end
212225
end
213226

214227
describe "#kind_of?" do

spec/rspec/active_model/mocks/stub_model_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,18 @@ def model_class
181181
end
182182
# rubocop:enable Lint/LiteralAsCondition
183183
end
184+
185+
it "works for multiple mocks of the same model" do
186+
foo = stub_model(MockableModel)
187+
bar = stub_model(MockableModel)
188+
baz = stub_model(MockableModelNoPrimaryKey)
189+
qux = stub_model(MockableModel)
190+
191+
expect(MockableModel === foo).to be(true)
192+
expect(MockableModel === bar).to be(true)
193+
expect(MockableModel === baz).to be(false)
194+
expect(MockableModelNoPrimaryKey === baz).to be(true)
195+
expect(MockableModel === qux).to be(true)
196+
end
184197
end
185198
end

0 commit comments

Comments
 (0)