@kyanny's blog

My life. Opinions are my own.

mock の使いどころが少しわかってきた

以前 [ruby] RUBY_PATCHLEVEL の値, [ruby][rr][rspec] mock と stub - HsbtDiary(2010-10-25) を読んで、おれも mock と stub をどう使い分けたらいいのかちゃんと理解できてないなーとずっともやもやしていた。で、今日仕事でテストを書いていたら(えぇ、ちゃんと書いてますよ。まだまだ全然足りないけどね) mock をちょっと上手い具合に使えたかな?という気がしたのでブログに書く。

仕事で書いたコードは ActionMailer のサブクラスに対するテストだったのだけど、それに似せて書いたサンプルコードがこれ。

mocha mock example · GitHub

#!/usr/bin/env ruby

require 'test/unit'
require 'mocha'

class User
  attr_accessor :user_id, :name, :payment_status

  def initialize(name)
    @user_id = self.object_id.abs.to_i
    @name = name
  end
end

class Mailer
  def deliver_all_payment_notify(users)
    users.each do |user|
      if user.payment_status == true
        deliver_payment_success_notify(user)
      else
        deliver_payment_fail_notify(user)
      end
    end
  end

  private
  def deliver_payment_success_notify(user)
  end

  def deliver_payment_fail_notify(user)
  end
end

if $0 == __FILE__
  eval(DATA.read)
end

__END__

class MailerTest < Test::Unit::TestCase
  def test_deliver_all_payment_notice
    melody = User.new('Melody')
    melody.payment_status = true

    nelson = User.new('Nelson')
    nelson.payment_status = false

    mailer = Mailer.new

    mailer.expects(:deliver_payment_success_notify).once.with(melody)
    mailer.expects(:deliver_payment_fail_notify).once.with(nelson)

    mailer.deliver_all_payment_notify([melody, nelson])
  end
end

Mailer クラスはパブリックメソッドをひとつ、プライベートメソッドをひとつもっていて、実際にメールを送る仕事はプライベートメソッドがやる(という仕様のつもり。空っぽだけど)で、パブリックメソッドにはユーザーのリストを渡して user.payment_status の値によってどういうメールを送るか条件分岐している。この条件分岐が期待したとおりに動くのかをテストしたいわけだ。

そんで上のようなテストを書いた。ここでは mocha というライブラリを利用した。実行環境は ruby 1.9.2p0 (2010-08-18 revision 29036) [i386-darwin9.8.0] で mocha のバージョンは 0.9.10 を使った。

キモは mailer.expects ではじまってる二行で、英語っぽく読むと「mailer は deliver_payment_success_notify というメソッドが melody をともなって一回呼ばれることを期待している」とか。で、テストが走ったときにこのテストケース内で deliver_payment_success_notify メソッドが呼ばれなかったり、二回以上呼ばれたり、引数が melody じゃなかったりすると fail する。

deliver_all_payment_notify メソッドのどこをテストしたいかというと「どのユーザーに対してどのメール配信メソッドが選ばれるのか」なので、それをテストコードとして書くとこんな風になるね、という。まぁあくまで例なので「こんなクソ設計しねーよ普通」とかかもしれませんが。

mocha をしばらく(今年の二月からなのでもうすぐ一年)仕事でテストコード書くときに使ってみた感想としては、テストに失敗したりエラーになったりしたときに何が起こってるのか、慣れるまではわかりづらかった。でも mock を使ったテストがビシッと成功すると、ビートマニア(古い)で難所を息継ぎ無しノーミスで乗り切ったときのような達成感が味わえる。あと、とても強力な道具を手に入れた気分になってモチベーションがあがる。メーリングリストはあまり活発ではないし更新も頻繁ではないけど継続してメンテナンスされていて、 API は安定しているというかむしろ枯れていておそらく今後後方互換性を無くすような大きな変更はないだろうと思うので、安心して利用できるんじゃないかなと思う。まぁふつうは RSpec 使うんでしょうけど・・・。