1. matsukaz

    Posted

    matsukaz
Changes in title
+minitestでモジュールメソッドにstubを使う
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,92 @@
+# minitestとは
+
+minitestはruby標準のテスティングフレームワーク。
+標準だけど、[いろいろ経緯があって](http://qiita.com/jnchito/items/ff4f7a23addbd8dbc460)、バージョンや対応状況がコロコロ変わっていまいち広まってないみたい。
+RSpecを使う人が多い中、minitestに関する情報はぐぐってもあんまりない状態(;´Д`)
+
+# モジュールメソッドにstubを使う方法
+
+minitestでmockやstubを使う方法はこちら。
+http://blog.willnet.in/entry/2012/12/05/004010
+
+ただモジュールメソッドに対して適用する方法はなかなか見つからなかった。
+いろいろ試してやっとできたので、紹介します。
+
+以下は指定桁のランダムなhexを生成するという、stub置き換え対象のモジュールメソッド。
+
+```rb
+require 'securerandom'
+
+module TestModule
+ module_function
+
+ def hex(length)
+ SecureRandom.hex(length).slice(0, length)
+ end
+end
+```
+
+こういうのをメソッドを利用したクラスをテストするとき、stubに置き換えないとランダムな値のハンドリングがやりづらいという話。
+以下がhexメソッドをstubに置き換えたテストケース。
+
+```rb
+require "minitest/autorun"
+
+describe TestModule, :hex do
+ describe "when length is passed" do
+ it "should return hex value" do
+
+ test_module_mock = MiniTest::Mock.new
+ test_module_mock.expect :call, "0123456789abcdef", [16]
+
+ TestModule.stub :hex, test_module_mock do
+ hex = TestModule.hex 16
+ hex.must_equal "0123456789abcdef"
+ test_module_mock.verify
+ end
+ end
+ end
+end
+```
+
+ポイントは、
+
+```
+test_module_mock.expect :call, "0123456789abcdef", [16]
+```
+
+といった形でMockオブジェクトに対して `:call` メソッドを定義するのと、
+
+```
+TestModule.stub :hex, test_module_mock
+```
+
+とstubに置き換えたいメソッドを指定するところ。
+なんで`:call`なのと思ったら、minitestのstubメソッドの実装にコメントで書いてあった。
+
+```rb
+ # Add a temporary stubbed method replacing +name+ for the duration
+ # of the +block+. If +val_or_callable+ responds to #call, then it
+ # returns the result of calling it, otherwise returns the value
+ # as-is. If stubbed method yields a block, +block_args+ will be
+ # passed along. Cleans up the stub at the end of the +block+. The
+ # method +name+ must exist before stubbing.
+```
+
+実装でいうとこのあたり
+
+```rb
+ metaclass.send :define_method, name do |*args, &blk|
+ ret = if val_or_callable.respond_to? :call then
+ val_or_callable.call(*args)
+ else
+ val_or_callable
+ end
+
+ blk.call(*block_args) if blk
+
+ ret
+ end
+```
+miniestに関してはほんとに情報少ないので、ちょっと探してだめなら実装見た方がよっぽど早そうです。
+