test と mock と

試験のバックトレイスって勉強になるねぇ。以下の試験を元に色々検証。

  def test_send_pings
    @article1.send_pings("example.com", "http://localhost/post/5?param=1")
    ping = Net::HTTP.pings.first
    assert_equal "localhost",ping.host
    assert_equal 80, ping.port
    assert_equal "/post/5?param=1", ping.query
    assert_equal "title=Article%201!&excerpt=body&url=example.com&blog_name=test%20blog", ping.post_data
  end

send_pings については昨晩のエントリでコード引用しているので略するが、引数に元記事の URL と trackback ping を送信したい記事の URL の配列が渡される (はず)。で、Ping のオブジェクトを作って、send_ping メソドを実行した後に DB に格納、という一連の処理となっている。
上記のコードはそれを試験するためのコード。
単体試験とゆー事で http なやりとりの mock も用意されていた。参考になる。

module Net
  remove_const "HTTP"
  class Request < Struct.new(:host, :port, :query, :post_data)
    def post(query, post)
      self.query = query
      self.post_data = post    
    end    
  end
  
  class Net::HTTP
      
    
    def self.start(host, port) 
      request = Request.new
      request.host = host
      request.port = port    
      
      @pings ||= []
      @pings << request

      yield request
    
    end
    
    def self.pings
      @pings
    end
    
end

とりあえず順を追って、試験のコードから何がどうなっているのか、と纏めておく。

  1. ArticleTest#test_send_pings から Article#send_pings 呼び出し。
  2. Article#send_pings では Ping オブジェクトを生成(url 属性に ping 送信先の URL を設定) し、Ping#send_ping を呼ぶ。
  3. uri と post するデータを適宜作成し、Net::HTTP.start 呼び出し。
  4. 上記の mock なコードに定義された start 実行。
    1. 引数で渡された host と port を Request クラスのインスタンスに設定
    2. 上記インスタンスを @pings 配列に push しつつ yield にも渡す
  5. start に渡しているブロックの引数は request インスタンス
  6. start なブロック中にて http.post (実は request.post) を呼び出し。
    1. Request#post では渡された query と post をインスタンスに設定
  7. Article#send_pings から戻ってきたら pings (mock なコードにて定義) 呼び出し
  8. Request インスタンスに設定された値を assert_equal で確認

これはこれは、なかなか。