RSpec の expect_any_instance_of でハマった
class Test def say_hoge hoge1 = Hoge.new hoge2 = Hoge.new hoge1.say hoge2.say end end class Hoge def say puts 'hoge' end end
こんな感じのクラスがあったとして、 Test#say_hoge で Hoge インスタンスが Hoge#say を1回ずつ実行していることをテストしたいとなったときに、以下のようなテストを書きました。
describe 'Test' do describe '#say_hoge' do it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do expect_any_instance_of(Hoge).to receive(:say).once test = Test.new test.say_hoge end end end
これを実行してみたところ、落ちてしまいました。
ああ、呼ばれている回数は全インスタンスの合計になるのか、なるほどね!とか思って、以下のように once を twice に変えてみました。
describe 'Test' do describe '#say_hoge' do it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do expect_any_instance_of(Hoge).to receive(:say).twice # 合計2回実行されるので twice に変えてみた test = Test.new test.say_hoge end end end
これを実行してみたところ、またまた落ちてしまいました。
なぜだと思ってエラーを見てみると、「Hoge#say を hoge2 が実行したんだけど、 hoge1 でもう実行されてるよ」 的なことが書いてありました。
1) Test #say_hoge Hoge instance call Hoge#say once Failure/Error: hoge2.say The message 'say' was received by #<Hoge:70248456891000 > but has already been received by #<Hoge:0x007fc7fca35518>
ためしに、以下のように hoge2.say を消して、 twice を once に戻してみました。
class Test def say_hoge hoge1 = Hoge.new hoge2 = Hoge.new hoge1.say # hoge2.say end end
describe 'Test' do describe '#say_hoge' do it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do expect_any_instance_of(Hoge).to receive(:say).once test = Test.new test.say_hoge end end end
これを実行すると、ちゃんと通りました。
わかったこと
おそらく、 expect_any_instance_of はどのインスタンスも対象にとるが、対象のインスタンスは1つに限るということなのだと思います。
どうするか
そのため、stub を使って Hoge.new でつくられるインスタンスを同じにして、そのインスタンスの Hoge#say が合計2回呼ばれるということをテストすることにしました。
class Test def say_hoge hoge1 = Hoge.new hoge2 = Hoge.new hoge1.say hoge2.say end end
describe 'Test' do describe '#say_hoge' do it 'Hoge インスタンス は Hoge#say を1回ずつ実行する (合計2回) ' do hoge = Hoge.new allow(Hoge).to receive(:new).and_return(hoge) expect(hoge).to receive(:say).twice test = Test.new test.say_hoge end end end
これを実行したところ無事通りました。
Finished in 0.01246 seconds (files took 0.42959 seconds to load) 1 example, 0 failures