@kyanny's blog

My thoughts, my life. Views/opinions are my own.

Mocha::Expectation#with(&matching_block)

の使い方を覚えた。 with にブロックをあたえると expects に指定したメソッドと同じ引数を受け取り、ブロックが評価される。ブロックが真を返すかどうかが影響するので、ブロックの中に assert を書いたりしないように注意。以下のてきとうな例では、 LWP::Simple.getprint メソッドの中で実行されるメソッド呼び出しが期待したとおりに動いているか、を、テストしているつもり。

https://gist.github.com/1099986

require 'net/http'

module LWP
  class Simple
    def self.getprint(url)
      uri = URI(url)
      Net::HTTP.start(uri.host, uri.port){ |http|
        http.get(uri.path).body
      }
    end
  end
end

if $0 == __FILE__
  eval DATA.read
end

__END__

require 'test/unit'
require 'mocha'

class TestLWPSimple < Test::Unit::TestCase
  def test_valid_path
    Net::HTTP.any_instance.expects(:get).with{ |path|
      path == '/a666666/about'
    }.returns(stub('response', :body))
    LWP::Simple.getprint("http://d.hatena.ne.jp/a666666/about")
  end
end


ちょっと脱線して、スタブとモックについて最近意識していること。というか以前に比べてそれらをどう使い分けるのか?についての自分なりの考え方が固まってきたので書いてみる。

スタブは単に「ここから先は無視してオッケー」という部分を無効化するくらいの気持ちで使ってる。上のコードを例にとると、 http.get() は HTTPResponse クラスのインスタンスを返すべきであり body というインスタンスメソッドを呼び出せるべきである、が今はそんなことはどうでもいいので、単に body メソッドの呼び出しでエラーにならないようにスタブしている。

モックは込み入っていて、あるメソッド foo が何らかのデータを加工して他のメソッド bar を呼び出すとき、「foo を呼び出したならば bar も呼び出されるべきである」ということをテストしたいし、むしろ「foo を呼び出したならば bar に渡すデータをただしく作成できるべきである(ついでに bar も呼び出されるべきである)」くらいの気持ちでモックを使うことが多い。

どちらも foo メソッドがおこなう処理を手続き的に見ていて、メソッド呼び出しに注目しているときはフロー制御的な側面から「foo メソッドはこういう順序で各種の手続きを実行します」ということをテストしようとしており、データに注目しているときはデータ処理的な側面から「foo メソッドはこういう手続きを経てこのようにデータを加工します」ということをテストしようとしている。という意識でおれはスタブとモックを使い分けていますよ、という話。

ここでさらに先日の RubyKaigi で発表されていた Fake を取り上げてその違いにも注目したいところなんだけどあいにく Fake が何かぼんやりとしかわかってないし実際に試してみたこともないのでそれは皆さんへの宿題にします!今後の楽しみにとっておきます...