helen's blog

ずっとおもしろいことしてたいな。

railsでモック使ってみた話

開発チームで
railsでモックの調査する人柱になってくれる人!という募集があり
人柱やりまーすと立候補したところから始まりました。

実は一回挫折していて気になっていたのと
ちょうど自分の作業に区切りがついたのでやってみました

モックとは

  • 本物のフリをする偽物のプログラム
  • 諸事情により本物のプログラムが使えない、使わない方が良い時に使われる

外部のAPIを使う場合などに使えます

使い方

モックが呼び出せるメソッドの設定

allow(モックオブジェクト).to receive(メソッド名)

オブジェクトの置き換え方

allow(実装を置き換えたいオブジェクト).to receive(置き換えたいメソッド名).and_return(返却したい値やオブジェクト)

実装例

例)商品に基づいてメーカーを取得する

spec
describe "商品に紐付いたメーカーを取得します" do
  before do
    # double()でモックを作ります
    # 文字列は省略可ですが、この文字列はテスト失敗時に表示されます
    maker_mock = double('maker mock test')
    # maker_mockがfind_with_itemsメソッドを呼び出せるようにします
    allow(maker_mock).to receive(:find_with_items)
  
    @makers = Makers.new
    # オブジェクトが呼ばれたらモックに置き換える
    allow(@makers).to receive(:find_with_items).and_return(maker_mock)
    # これでmaker_mockはfind_with_itemsを呼び出せるようになり
    # @makersでmaker_mockが呼ばれる
  end

  context "正常系" do
    it "取得した商品のメーカーが存在すること" do
      expect(@maker.find_with_items).to be_present
    end
  end
end
model
class Makers < ActiveRecord::Base
 def find_with_items
   maker.all
 end
end

実行結果

# テスト成功例
$ bundle exec rspec spec/models/makers_spec.rb
.
Finished in 0.02464 seconds (files took 1.97 seconds to load)
1 example, 0 failures
 
# テスト失敗例
$ bundle exec rspec spec/models/makers_spec.rb
F
Failures:
  1) 商品に紐付いたメーカーを取得します 正常系 取得した商品のメーカーが存在すること
     Failure/Error: expect(@makers.find_with_itemss).to be_present
     # ここにdouble()で入れた文字列が表示されます
       expected `#<Double "maker mock test">.present?` to return false, got true 
     # ./spec/models/makers_spec.rb:27:in `block (4 levels) in <top (required)>'
Finished in 0.02958 seconds (files took 1.9 seconds to load)
1 example, 1 failure

モックのメソッドが呼ばれているか検証する

expect(モックオブジェクト).to receive(メソッド名)

expectの場合はそのメソッドが呼ばれないとテスト失敗になります。

before do
  maker_mock = double('maker mock test')
  # maker_mockがfind_with_itemsメソッドを呼び出せるようにします
  allow(maker_mock).to receive(:find_with_items)
  
  @makers = Makers.new
  # 変更点:allowをexpectにします
  expect(@makers).to receive(:find_with_items).and_return(makers_mock)
end

実装を置き換えるのか、メソッドの呼び出しも検証するのかでallowとexpectを使い分けます。

今まとめてて思ったけどちょっといろいろ怪しいんで
出社したらプルリクWIPにして再調査しようかなというところです...
うーん、難しい

参考
Ruby - 使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」 - Qiita
Expecting messages - Basics - RSpec Mocks - RSpec - Relish
Rspec3のexpectとallowの違い - JanGaJan.com