メインコンテンツまでスキップ

スコープ

すべての rspec-mocks の構造物は、各例ごとにライフサイクルを持ちます。メッセージの期待値は、各例の後で検証されます。ダブル、メソッドスタブ、スタブ化された定数などは、各例の後にすべてクリーンアップされます。これにより、各例を独立して実行し、任意の順序で実行できるようになります。

before(:example) フック内でダブル、スタブ、メッセージの期待値を設定することは完全に問題ありません。なぜなら、このフックは例のスコープで実行されるからです。

before(:example) do
allow(MyClass).to receive(:foo)
end

before(:context) は個々の例のスコープ外で実行されるため、rspec-mocks の機能の使用はサポートされていません。ただし、RSpec::Mocks.with_temporary_scope { } を使用して、before(:context) フックを含む任意のコンテキストで一時的なスコープを作成することはできます。

before(:context) フック内でダブルを作成できません

次の内容で "before_context_spec.rb" という名前のファイルがあるとします。

RSpec.describe "Creating a double in a before(:context) hook" do
before(:context) do
@dbl = double(:foo => 13)
end

it "fails before it gets to the examples" do
expect(@dbl.foo).to eq(13)
end
end

rspec before_context_spec.rb を実行すると、

次のエラーが発生するはずです。

rspec-mocks のダブルや部分的なダブルをテストライフサイクルの外で使用することはサポートされていません。

with_temporary_scope を使用して before(:context) フック内でダブルを作成・使用する

次の内容で "with_temporary_scope_spec.rb" という名前のファイルがあるとします。

RSpec.describe "Creating a double in a before(:context) hook" do
before(:context) do
RSpec::Mocks.with_temporary_scope do
dbl = double(:foo => 13)
@result = dbl.foo
end
end

it "allows a double to be created and used from within a with_temporary_scope block" do
expect(@result).to eq(13)
end
end

rspec with_temporary_scope_spec.rb を実行すると、すべての例がパスするはずです。

ダブルは別の例で再利用できません

次の内容で "leak_test_double_spec.rb" という名前のファイルがあるとします。

class Account
class << self
attr_accessor :logger
end

def initialize
@balance = 0
end

attr_reader :balance

def credit(amount)
@balance += amount
self.class.logger.log("Credited $#{amount}")
end
end

RSpec.describe Account do
it "logs each credit" do
Account.logger = logger = double("Logger")
expect(logger).to receive(:log).with("Credited $15")
account = Account.new
account.credit(15)
end

it "keeps track of the balance" do
account = Account.new
expect { account.credit(10) }.to change { account.balance }.by(10)
end
end

rspec leak_test_double_spec.rb を実行すると、次の出力とともに失敗するはずです。

| 2 examples, 1 failure                                                                                                   |
| |
| 1) Account keeps track of the balance |
| Failure/Error: self.class.logger.log("Credited $#{amount}") |
| #<Double "Logger"> was originally created in one example but has leaked into another example and can no longer be used. |