1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【RSpec】モックとスタブについて

Last updated at Posted at 2021-10-01

学習のアウトプットとして投稿しています。
何かご指摘がございましたらお願いいたします。

モックとスタブとは

■ モック(mock)
モックは本物のオブジェクトのふりをするオブジェクトのこと。テストで用いられる。
これまで、FactoryBotや素のRubyを使って作成していたテストデータの代わりになるもの。
両者の違いはDBにアクセスするかしないか。モックはDBにアクセスしない。
そのため、テスト時間を短縮できる
テスト対象でないオブジェクトなどは、モックで管理した方がよい場合がある。
この場合、テスト対象が関連オブジェクトの実装に影響されにくくなり、管理しやすいテストコードとなる

■ スタブ(stub)
スタブはオブジェクトのメソッドを上書きして、事前に返して欲しい値を設定することができる。
なので、スタブはテストでダミーメソッドとして使うことができる。

使い方

user.rb
class User < ApplicationRecord
  has_one :profile

  delegate :name, to: :profile
end
profile.rb
class Profile < ApplicationRecord
  has_one :user

  def name
    [first_name, last_name].join(" ")
  end
end

ここでUserモデルのnameメソッドをモックとスタブを使ってテストした場合

spec/models/example_spec.rb
RSpec.describe User, type: :model do
  # モックを使う
  let(:profile) { double("profile", name: "Test User") }

  it "ユーザーのフルネームを返す" do
    user = User.new
    # スタブを使う
    allow(user).to receive(:profile).and_return(profile)
    expect(user.name).to eq "Test User"
  end
end

profileにモックを使い、引数にメソッドが呼ばれた時の返り値も設定しておく。
userにはprofileを受け取ったら、モックのprofileを返すようスタブを使い設定する。

こうすることで、本来userオブジェクトはprofileを受け取ったらDBに問い合わせてprofile_idからprofileをとってくるところを、スタブを使いモックのprofileを返すと設定したのでDBに問い合わせることなく、実行スピードが早くなる。
さらに、このUserモデルのテストは、モックを使ったことでprofileのnameメソッドの実装に依存しなくなるため、profileモデルの実装から影響を受けにくくなり管理しやすいテストになった。

注意点

このテストは、モックのprofileしか見ていないため、例えばprofileモデルのnameメソッドの名前が変わったり、削除されたりしてもこのテストは通ってしまう。
コードの変更によるバグなどはテストでキャッチするべきなので、このままではよくない。
この問題に対処するため、検証機能付きのモックを使う。検証機能付きのモックは引数で与えられたモデルのインスタンスと同じように振る舞うので、nameメソッドがなかった場合はエラーを出してくれる。

先ほどのテストに検証機能付きのモック(instance_double)を使った場合
※引数のprofileは大文字で記載する

spec/models/example_spec.rb
RSpec.describe User, type: :model do
  # 検証機能付きのモックを使う(引数のProfileは大文字で記載する)
  let(:profile) { instance_double("Profile", name: "Test User") }

  it "ユーザーのフルネームを返す" do
    user = User.new
    # スタブを使う
    allow(user).to receive(:profile).and_return(profile)
    expect(user.name).to eq "Test User"
  end
end

まとめ

テストデータの作成はFactoryBotやRubyでしか作成したことがなかったので、テスト時間が早くなるということでオリジナルアプリのテスト作成にモック導入を検討してみようかなと思いました。ただテスト初心者なので使いやすそうなinstance_doubleからやっていきたいですね。。。

参考

Everyday Rails - RSpecによるRailsテスト入門
使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」
Railsガイド 3.4 メソッドの委譲

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?