Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

RSpec のテストダブルで呼び出しているクラスメソッドの変更を検出する方法について

f:id:tbpg:20170324030430p:plain

RSpec の class_double で呼び出しているクラスやメソッドの変更を検出する方法についてまとめます。

テストパターン

クラスもメソッドも存在するケース

require "spec_helper"

class Hoge
  def self.hoge
    "hoge"
  end
end

class HogeUser
  def initialize(hoge)
    @hogea = hoge
  end

  def use
    @hogea.hoge
  end
end

describe HogeUser do
  it do
    hoge = class_double("Hoge")
    expect(hoge).to receive(:hoge).and_return("aaaaaa")

    user = HogeUser.new(hoge)
    expect(user.use).to eq("aaaaaa")
  end
end
  • 結果
$ rspec spec/double_exist_method_spec.rb
.

Finished in 0.0056 seconds (files took 0.09756 seconds to load)
1 example, 0 failures

mocks.verify_doubled_constant_names = false の場合

  • spec_helper.rb
  # 略
  config.mock_with :rspec do |mocks|
    # ※設定を省略した場合はデフォルトで false になります
    mocks.verify_doubled_constant_names = false
  end
  # 略

class_double でクラスが存在しないケース

require "spec_helper"

# class Hoge
  # def self.hoge
    # "hoge"
  # end
# end

class HogeUser
  def initialize(hoge)
    @hogea = hoge
  end

  def use
    @hogea.hoge
  end
end

describe HogeUser do
  it do
    hoge = class_double("Hoge")
    expect(hoge).to receive(:hoge).and_return("aaaaaa")

    user = HogeUser.new(hoge)
    expect(user.use).to eq("aaaaaa")
  end
end
  • 結果

正常終了してしまいます。
モック対象の変更やタイポをテストで検出することができません。

$ rspec spec/double_not_exist_class_spec.rb
.

Finished in 0.00536 seconds (files took 0.12313 seconds to load)
1 example, 0 failures

class_double でメソッドが存在しないケース

require "spec_helper"

class Hoge
  # def self.hoge
    # "hoge"
  # end
end

class HogeUser
  def initialize(hoge)
    @hogea = hoge
  end

  def use
    @hogea.hoge
  end
end

describe HogeUser do
  it do
    hoge = class_double("Hoge")
    expect(hoge).to receive(:hoge).and_return("aaaaaa")

    user = HogeUser.new(hoge)
    expect(user.use).to eq("aaaaaa")
  end
end
  • 結果

メソッドが存在しないとエラーになります。
メソッド名の変更をエラーとして検出できます。

$ rspec spec/double_not_exist_class_spec.rb
F

Failures:

  1) HogeUser
     Failure/Error: hoge = class_double("Hoge")
       "Hoge" is not a defined constant. Perhaps you misspelt it? Disable check with `verify_doubled_constant_names` configuration option.
     # ./spec/double_not_exist_class_spec.rb:21:in `block (2 levels) in <top (required)>'

Finished in 0.00054 seconds (files took 0.10972 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/double_not_exist_class_spec.rb:20 # HogeUser

mocks.verify_doubled_constant_names = true の場合

  • spec_helper.rb
  # 略
  config.mock_with :rspec do |mocks|
    mocks.verify_doubled_constant_names = true
  end
  # 略

class_double でクラスが存在しないケース

ソースコードverify_doubled_constant_names = false のケースと同じ

  • 結果

クラスが存在しないとエラーになります。
クラス名の変更をエラーとして検出できます。

$ rspec spec/double_not_exist_class_spec.rb


Failures:

  1) HogeUser
     Failure/Error: hoge = class_double("Hoge")
       "Hoge" is not a defined constant. Perhaps you misspelt it? Disable check with `verify_doubled_constant_names` configuration option.
     # ./spec/double_not_exist_class_spec.rb:21:in `block (2 levels) in <top (required)>'

Finished in 0.00054 seconds (files took 0.10972 seconds to load)
1 example, 1 failure

Failed examples:

class_double でメソッドが存在しないケース

  • ソースコードmocks.verify_doubled_constant_names = false の例と同じなので省略
  • 実行結果は mocks.verify_doubled_constant_names = false の例と同じなので省略