rspecで特定のメソッドが呼ばれているのかテストする

2017/08/25

とあるメソッドの中で特定のメソッドがちゃんと呼ばれているのかテストしたいことがしばしばあって、どうやって書けば良いかわからなかったのだけど、最近なんとなく書けてるっぽい方法がわかったので個人的にメモしておく。
全然詳しくないので果たしてこれで良いのかは不明。書かないよりは断然マシという精神で気持ちを保っている。

テスト対象として下記のようなHogeクラスを準備する。

class Hoge
  def foo
    self.bar
  end

  def bar
    "hey"
  end
end

fooの中でちゃんとbarが呼ばれてるのかをテストする。

# /lib/hoge.rb
require "spec_helper"
require "hoge"

describe "hoge" do
  context ".foo" do
    let(:hoge) { Hoge.new }
    it "called .bar once" do
      expect(hoge).to receive(:bar).once.and_return(true)
      expect(hoge.foo).to eq true
    end
  end
end

# Finished in 0.00768 seconds (files took 0.08876 seconds to load)
# 1 example, 0 failures

onceをtwiceに書き換えてみるとちゃんとコケてるので動いてるっぽいことがわかる。

# /spec/lib/hoge.rb
require "spec_helper"
require "hoge"

describe "hoge" do
  context ".foo" do
    let(:hoge) { Hoge.new }
    it "called .bar once" do
      expect(hoge).to receive(:bar).twice.and_return(true)
      expect(hoge.foo).to eq true
    end
  end
end

# Failures:
#
#   1) hoge .foo called .bar once
#      Failure/Error: expect(hoge).to receive(:bar).twice.and_return(true)
#
#        (#<Hoge:0x007fae85993b18>).bar(*(any args))
#            expected: 2 times with any arguments
#            received: 1 time with any arguments
#
# Finished in 0.00615 seconds (files took 0.07411 seconds to load)
# 1 example, 1 failure

引数も確認したければwithとか生やせば良い。

# /lib/hoge.rb
class Hoge
  def foo
    self.bar("tanaka", "hello world")
  end

  def bar(name, message)
    "hey #{name}, #{message}"
  end
end
# /spec/lib/hoge_spec.rb
require "spec_helper"
require "hoge"

describe "hoge" do
  context ".foo" do
    let(:hoge) { Hoge.new }
    let(:name) { "tanaka" }
    let(:message) { "hello world" }
    it "called .bar once" do
      expect(hoge).to receive(:bar).with(name, message).once.and_return(true)
      expect(hoge.foo).to eq true
    end
  end
end

# Finished in 0.00713 seconds (files took 0.07314 seconds to load)
# 1 example, 0 failures

引数足りないと怒られる。

# /spec/lib/hoge_spec.rb
require "spec_helper"
require "hoge"

describe "hoge" do
  context ".foo" do
    let(:hoge) { Hoge.new }
    let(:name) { "tanaka" }
    let(:message) { "hello world" }
    it "called .bar once" do
      expect(hoge).to receive(:bar).with(name, message).once.and_return(true)
      expect(hoge.foo).to eq true
    end
  end
end

# Failures:
#
#   1) hoge .foo called .bar once
#      Failure/Error: expect(hoge).to receive(:bar).with(name).once.and_return(true)
#        Wrong number of arguments. Expected 2, got 1.
#      # ./spec/lib/hoge_spec.rb:10:in `block (3 levels) in <top (required)>'
#
# Finished in 0.00683 seconds (files took 0.075 seconds to load)
# 1 example, 1 failure