Posted at

Rails minitest Mockで例外発生パターンのテスト

@koshi_life です。

Rails 標準のテストフレームワーク minitest にて Mock側で例外を発生させるテストケースの書き方の備忘です。


前提


  • Rails 5.2.3

  • Ruby 2.6.0

  • minitest 5.11.3


検証対象モジュール

api_clientをコンストラクタ引数で取り、テスト対象のcoutup関数は、引数のitem_idをAPI経由(#get)で取得し、値に1加えてAPI経由(#update)で更新する簡素な内容です。


fuga.rb

class Fuga

def initialize(api_client)
@api_client = api_client
end

def countup(item_id)
error_code = 0

# APIでItem取得
begin
item = @api_client.get(item_id)
rescue StandardError => e
# Case1: テストではココの分岐に入ることを確認します
error_code = 1
return false, error_code
end

# APIでItemの値更新
begin
@api_client.update(item_id, item[:val] + 1)
rescue StandardError => e
# Case2: テストではココの分岐に入ることを確認します
error_code = 2
return false, error_code
end
return true, error_code
end
end



テストコード

api_clientをMock化します。api_client#get, api_client#updateコール時に例外を捕捉する分岐を通るケースを書いてみます。


Mock#expect-instance_method より

#expect(name, retval, args = [], &blk) ⇒ Object


&blkの Procを利用して例外を発生させることができます。


fuga_test.rb

require 'test_helper'

class FugaTest < ActiveSupport::TestCase
test 'Case1: api_client#getで例外発生' do
item_id = 'id-1'
retval_get = { val: 1 }
args_get = [item_id]

api_mock = MiniTest::Mock.new
api_mock.expect(:get, retval_get) do |args_update|
raise StandardError('Mock#getで例外発生')
end

# 検証したいモジュールを実行します
fuga = Fuga.new(api_mock)
result, error_code = fuga.countup(item_id)
assert_equal(false, result)
assert_equal(1, error_code)

# Mockに送信されたデータを検証します。
api_mock.verify
end

test 'Case2: api_client#updateで例外発生' do
item_id = 'id-2'
retval_get = { val: 2 }
args_get = [item_id]

retval_update = nil
args_update = [item_id, 3]

api_mock = MiniTest::Mock.new
api_mock.expect(:get, retval_get, args_get)
api_mock.expect(:update, retval_update) do |args_update|
raise StandardError('Fuga#updateで例外発生')
end

# 検証したいモジュールを実行します
fuga = Fuga.new(api_mock)
result, error_code = fuga.countup(item_id)
assert_equal(false, result)
assert_equal(2, error_code)

# Mockに送信されたデータを検証します。
api_mock.verify
end

test '参考: 比較用に正常系' do
item_id = 'id-3'
retval_get = { val: 3 }
args_get = [item_id]

retval_update = true
args_update = [item_id, 4]

api_mock = MiniTest::Mock.new
api_mock.expect(:get, retval_get, args_get)
api_mock.expect(:update, retval_update, args_update)

# 検証したいモジュールを実行します
fuga = Fuga.new(api_mock)
result, _ = fuga.countup(item_id)
assert_equal(true, result)

# Mockに送信されたデータを検証します。
api_mock.verify
end
end



参考