@koshi_life です。
最近、Ruby関連のプロジェクトでテスト書いたり、リファクタしたりしています。そこでPrivate宣言したメソッドのテストの書き方を調べたので備忘です。
結論 Object#send を使う
Object#send メソッドを用いることで、Private宣言したメソッドをクラス外から呼び出しが可能です。
ちなみに、privateメソッドがコールできてしまうRubyの言語仕様/背景について以下エントリが参考になりました。
Ruby の private と protected 。歴史と使い分け
前提
- Rails 5.2.3
- Ruby 2.6.0
- minitest 5.11.3
対象クラス
hoge.rb
class Hoge
# クラスメソッド
def self.pr_class_method(arg1, arg2)
"pr_class_method arg1=#{arg1} arg2=#{arg2}"
end
# クラスメソッド (キーワード引数)
def self.pr_class_method_kw(arg1:, arg2:)
"pr_class_method_kw arg1=#{arg1} arg2=#{arg2}"
end
# クラスメソッド (Block利用)
def self.pr_class_method_blk(arg1, &blk)
blk.call(arg1) if block_given?
"pr_class_method_blk arg1=#{arg1}"
end
private_class_method :pr_class_method,
:pr_class_method_kw,
:pr_class_method_blk
private
# インスタンスメソッド
def pr_instance_method(arg1, arg2)
"pr_instance_method arg1=#{arg1} arg2=#{arg2}"
end
end
send検証
.rb
$ rails c
# <インスタンスメソッド> 普通に呼ぶと NoMethodError エラー
> hoge = Hoge.new
> hoge.pr_instance_method(1,2)
# NoMethodError (private method `pr_instance_method' called for #<Hoge:0x00007ff7501ab338>)
# <インスタンスメソッド> sendで呼ぶ
> hoge.send(:pr_instance_method, 1, 2)
=> "pr_instance_method arg1=1 arg2=2"
# <クラスメソッド> 普通に呼ぶと NoMethodError エラー
> Hoge.pr_class_method(1, 2)
# NoMethodError (private method `pr_class_method' called for Hoge:Class)
# <クラスメソッド> sendで呼ぶ
> Hoge.send(:pr_class_method, 1, 2)
=> "pr_class_method arg1=1 arg2=2"
# <クラスメソッド> sendで呼ぶ (keyword引数)(blk引数)
> Hoge.send(:pr_class_method_kw, {arg1:'kw1', arg2:'kw2'})
=> "pr_class_method_kw arg1=kw1 arg2=kw2"
# <クラスメソッド> sendで呼ぶ (blk引数)
> Hoge.send(:pr_class_method_blk, 'blk-arg1') {|arg1| puts "it is block. arg1=#{arg1}"}
it is block. arg1=blk-arg1
=> "pr_class_method_blk arg1=blk-arg1"
テストコード
hoge_test.rb
require 'test_helper'
class HogeTest < ActiveSupport::TestCase
test 'Private Methodのテスト' do
# インスタンスメソッド
hoge = Hoge.new
actual1 = hoge.send(:pr_instance_method, 'i-A1', 'i-A2')
assert_equal('pr_instance_method arg1=i-A1 arg2=i-A2', actual1)
# クラスメソッド
actual2 = Hoge.send(:pr_class_method, 'c-A1', 'c-A2')
assert_equal('pr_class_method arg1=c-A1 arg2=c-A2', actual2)
# クラスメソッド (キーワード引数)
actual3 = Hoge.send(:pr_class_method_kw, { arg1: 'ckw-A1', arg2: 'ckw-A2' })
assert_equal('pr_class_method_kw arg1=ckw-A1 arg2=ckw-A2', actual3)
end
end