【ポンコツ版】.mock_in_sequence を作ってみました
追記 (2022/6/4):重大な欠陥が発覚しました。引数のある場合、正常に機能しません。修正を検討しなければいけなくなりました。
追記 (2022/6/4):修正を施した【陳謝版】は、こちら。
前回の記事のベタな解決法をmoduleにまとめてみました。
module MockInSequence
def mock_in_sequence(target_method, exp_sequence_args)
act_sequence = Array.new
mocks = Hash.new
exp_sequence = exp_sequence_args.map(&:first)
exp_sequence_args.reverse! # エラーが起きた際、レベルの深さが表示されるので、それに合わせるため処理順序を反転
stub_stream = "self.#{target_method};"
exp_sequence_args.each do |(name, retval, args)|
mocks[name] = MiniTest::Mock.new
if args.nil?
mocks[name].expect(:call, retval) { act_sequence << name }
else
mocks[name].expect(:call, retval) { act_sequence << name; args }
end
stub_stream = "self.stub(#{name.inspect}, mocks[#{name.inspect}]) do; #{stub_stream} end;"
end
eval(stub_stream) # 本能的にこの処理は宜しくないと思います。
# 実行結果を収めたハッシュをMockVerifyに食べさせてインスタンスにして返しています。
MockVerify.new({ mocks: mocks, expected: exp_sequence, actual: act_sequence })
end
class MockVerify
def initialize(results)
@mocks = results[:mocks]
@expected = results[:expected]
@actual = results[:actual]
end
def verify(test_obj)
@mocks.values.map(&:verify)
test_obj.assert_equal @expected,
@actual,
'note: not expected the order of sequence.'
end
end
end
こんな感じで以下のように使います。
不細工ですが、シンプルにまとまっていることは確かではないでしょうか。
require 'minitest/autorun'
class MyClassTest < Minitest::Test
def setup
@my_class = MyClass.new
end
def test_show
@my_class.extend MockInSequence # ここで、モジュールをオブジェクトにエクステンドします。
exp_sequence_args = [
[:show_header, true],
[:show_details, true],
[:show_footer, true]
]
results = @my_class.mock_in_sequence(:show, exp_sequence_args)
results.verify(self) # 魔法の呪文selfが必要です。
end
end
class MySubClassTest < Minitest::Test
def setup
@my_sub_class = MySubClass.new
end
def test_show
@my_sub_class.extend MockInSequence # ここで、モジュールをオブジェクトにエクステンドします。
condition = false
exp_sequence_args = [
[:show_header, true],
[:show_footer, true],
[:show_details, true, [condition]]
]
results = @my_sub_class.mock_in_sequence(:show, exp_sequence_args)
results.verify(self) # 魔法の呪文selfが必要です。
end
end
module内部のポンコツ加減も相当なものですが、MiniTest内での
results.verify(self)のselfって、何?
自分で作っておいてなんですが、不気味過ぎです。。。
module内のassert_equalを動かすためだけにselfを召喚してしまっています。
美しくない。。。ひじょーに不細工。
誰か良きアドバイスを。。。
module内でも怪しいことをしております。
eval(stub_stream)
悪魔の呪文evalを唱えてしまっています。
もっとプログラミング・スキルがあれば、なんとかなるのでしょうけど。
誰か良きアドバイスを。。。いや、助けて!
とりあえず、「動けば正義!」と自分に言い聞かせて生き抜くことにします。まぁ、ボチボチ、ダラダラと今後も続けていく予定です。
この記事が気に入ったらサポートをしてみませんか?