最近モナド、継続に続く第三の悟りを体験した。それが「モックとスタブの違い」
自分の言葉で表すとすれば、
テスト関心空間の内側にあるのがモックで外側がスタブ
といったところか。
発端はPython Testing: Beginnerでmockerを使っていたのだけど、verifyメソッドの存在意義がよく分からなかった(モックとスタブの違いを理解した今なら、verify必要に決まってんじゃんと言えるわけだが)。
悟りに至るまでにいくつかのサイトを読んだ。
スタブに関しては割りと容易に理解できる。モックとスタブの違いなんかに書いてあるように要するにスタブアウトですね。外界とのインタラクションを絶ち切って(debouple)状態にテストの関心を集中するわけだ。
で、問題はモック。Mock と Stub についてによると
Mock と Stub の違いはテストの観点の違いです。相互作用(振る舞い)中心のテストに利用するのがMockで、状態中心のテストに利用するのがStubです。
これだけだとわからないが、次のパラグラフを読むと
相互作用中心のテストとはテスト対象のシステムと外部のコンポーネントとの間で正しいやり取りがされるかのテスト、いわばプロトコルのテストです。外部のコンポーネントは Mock により置き換えられ、システムから正しい呼び出しがなされているかを監視します。
この文章でモックが何をデカップリングしようとするのかがおぼろげながら見えてきます。
したがって、例えばWebアプリの Controller の単体テストにおいて相互作用中心のテストが正しく行われてパスしているならば、Model が正しく実装された時に Controller が正しく動作するということが、Model が実装されなくても保障されます。
つまり、モックオブジェクトが期待されたとおり過不足なく呼び出されているかを調べるverifyはモックのテストにおいては重要な機能なわけだ。
実際にモックのテストを見ていると、内部の動作知ってないとテストかけないじゃんと思うんだけど、それはモックが必要な場面、モックが有効な場面に書いてあった。
でも、話を聞く限りだとモックというのはテスト対象の実装の中の処理の流れを追う物のようなので、それじゃブラックボックステストにならないじゃないかと思った。それをそのまま言ったら、確かにテストはできるだけブラックボックステストになってた方がいいけど、機能テストやインテグレーションテストのような粒度の大きな単位のテストでは、処理の中で起こる様々な出来事や副作用を色々モニタリングして、すべての処理が期待通りに動いているかどうかを検証しないといけないから、必然的にホワイトボックステストにならざるを得ないと言われた。
僕の場合はここまで読んだら、あーモックとスタブの違いってそういうことなのか!となったので参考になればと思い、メモっておく。