任何实例
rspec-mocks 提供了两种方法,allow_any_instance_of
和 expect_any_instance_of
,它们允许你存根或模拟类的任何实例。它们用于代替 allow
或 expect
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
expect_any_instance_of(Widget).to receive(:name).and_return("Wobble")
这些方法将适当的存根或期望添加到 Widget
的所有实例。
你也可以以相同的方式 配置响应。
此功能在处理遗留代码时有时很有用,但总的来说,我们出于多种原因不鼓励使用它。
rspec-mocks
API 是为单个对象实例设计的,但此功能适用于整个对象类。因此,存在一些语义上令人困惑的边缘情况。例如,在expect_any_instance_of(Widget).to receive(:name).twice
中,不清楚是否期望特定实例接收name
两次,或者是否期望总共接收两次。(前者是正确的。)- 使用此功能通常是一种设计气味。可能是你的测试试图做太多,或者被测对象过于复杂。
- 它是
rspec-mocks
中最复杂的功能,历史上收到了最多的错误报告。(核心团队中没有人积极使用它,这并没有帮助。)
使用 allow_any_instance_of
存根方法
给定一个名为“example_spec.rb”的文件,其中包含
RSpec.describe "allow_any_instance_of" do
it "returns the specified value on any instance of the class" do
allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)
o = Object.new
expect(o.foo).to eq(:return_value)
end
end
当我运行 rspec example_spec.rb
那么所有示例都应该通过。
使用 allow_any_instance_of
存根多个方法
给定一个名为“example_spec.rb”的文件,其中包含
RSpec.describe "allow_any_instance_of" do
context "with receive_messages" do
it "stubs multiple methods" do
allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar')
o = Object.new
expect(o.foo).to eq('foo')
expect(o.bar).to eq('bar')
end
end
end
当我运行 rspec example_spec.rb
那么所有示例都应该通过。
使用特定参数存根类的任何实例
给定一个名为“example_spec.rb”的文件,其中包含
RSpec.describe "allow_any_instance_of" do
context "with arguments" do
it "returns the stubbed value when arguments match" do
allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one)
allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two)
o = Object.new
expect(o.foo(:param_one, :param_two)).to eq(:result_one)
expect(o.foo(:param_three, :param_four)).to eq(:result_two)
end
end
end
当我运行 rspec example_spec.rb
那么所有示例都应该通过。
块实现将接收器作为第一个参数传递
给定一个名为“example_spec.rb”的文件,其中包含
RSpec.describe "allow_any_instance_of" do
it 'yields the receiver to the block implementation' do
allow_any_instance_of(String).to receive(:slice) do |instance, start, length|
instance[start, length]
end
expect('string'.slice(2, 3)).to eq('rin')
end
end
当我运行 rspec example_spec.rb
那么所有示例都应该通过。
使用 expect_any_instance_of
对任何实例设置消息期望
给定一个名为“example_spec.rb”的文件,其中包含
RSpec.describe "expect_any_instance_of" do
before do
expect_any_instance_of(Object).to receive(:foo)
end
it "passes when an instance receives the message" do
Object.new.foo
end
it "fails when no instance receives the message" do
Object.new.to_s
end
end
当我运行 rspec example_spec.rb
那么它应该失败,并显示以下输出
2 个示例,1 个失败 |
应该只有一个实例收到以下消息,但没有收到:foo |
指定与 allow_any_instance_of
结合使用时的多个调用以获得不同的返回值
使用 allow_any_instance_of
的多调用功能会导致在每个实例上配置多调用的行为。因此,每个单独的实例将按指定顺序返回配置的返回值,然后开始重复最后一个值。
给定一个名为“multiple_calls_spec_with_allow_any_instance_of.rb”的文件,其中包含
class SomeClass
end
RSpec.describe "When the method is called multiple times on different instances with allow_any_instance_of" do
it "demonstrates the mocked behavior on each instance individually" do
allow_any_instance_of(SomeClass).to receive(:foo).and_return(1, 2, 3)
first = SomeClass.new
second = SomeClass.new
third = SomeClass.new
expect(first.foo).to eq(1)
expect(second.foo).to eq(1)
expect(first.foo).to eq(2)
expect(second.foo).to eq(2)
expect(first.foo).to eq(3)
expect(first.foo).to eq(3) # repeats last value from here
expect(second.foo).to eq(3)
expect(second.foo).to eq(3) # repeats last value from here
expect(third.foo).to eq(1)
expect(third.foo).to eq(2)
expect(third.foo).to eq(3)
expect(third.foo).to eq(3) # repeats last value from here
end
end
当我运行 rspec multiple_calls_spec_with_allow_any_instance_of.rb
那么所有示例都应该通过。