Mochaのstub_everythingは強力
Jay Fields' Thoughts: Testing: Mocha's stub_everything method
stub_everythingの強力さについて。
stub_everythingメソッドは、どんなメソッドを呼び出してもnilを返すオブジェクトを生成する。なんていうか、幽霊…みたいな、透明人間みたいなオブジェクト。
stub(:meth => retval)同様「stub_everything(:meth => retval)」のように特定のメソッド呼び出しの返り値を設定することもできる。stubだと特定のメソッドを偽装するだけだが、stub_everythingはその他のメソッドは素通りするようになる。
こんなコードを例とする。
class ReservationService # implementation end class MaidService # implementation end class VipService # implementation end class HotelRoom def book_for(customer) reservation = ReservationService.reserve_for(customer, self) MaidService.notify(reservation) if reservation.tomorrow? VipService.notify(reservation) if reservation.for_vip? reservation.confirmation_number end end
で、stub_everythingの実例。
require 'test/unit' require 'rubygems' require 'mocha' class HotelRoomTests < Test::Unit::TestCase def test_book_for_reserves_via_ReservationService room = HotelRoom.new ReservationService.expects(:reserve_for).with(:customer, room).returns(stub_everyt room.book_for(:customer) end end
room.book_for(:customer)を呼んだとき、「ReservationService.reserve_for(:customer, room)」が呼ばれていることをテストする。
「ReservationService.reserve_for(:customer, room)」の返り値をstub_everythingにすると、そのオブジェクトにたいする一切の操作を無視できる。
require 'test/unit' require 'rubygems' require 'mocha' class HotelRoomTests < Test::Unit::TestCase def test_book_for_reserves_via_ReservationService reservation = stub_everything(:confirmation_number => "confirmation number") ReservationService.stubs(:reserve_for).returns(reservation) assert_equal "confirmation number", HotelRoom.new.book_for(:customer) end end
返り値がconfirmation numberであることをテストする。
「reservation.confirmation_number(*args) == "confirmation number"」かつ「その他のメソッドはnilを返す」オブジェクトreservationを偽装する。
「ReservationService.reserve_for(*args)」の返り値をその偽装オブジェクトにする。
require 'test/unit' require 'rubygems' require 'mocha' class HotelTests < Test::Unit::TestCase def test_name_is_set_from_options hotel = stub_everything hotel.expects(:name=).with "Marriott" Hotel.stubs(:new).returns hotel Hotel.parse("Marriott||") end end class Hotel def self.parse(attributes) hotel = self.new hotel.name, hotel.location, hotel.capacity = attributes.split("|") hotel end end
これはHotelオブジェクトすら偽装している、ガチガチのスタブだ。ここでテストするのは、「hotel.name == "Marriott"」であることのみだ。「hotel.location」や「hotel.capacity」は不要なのであえてスタブにしているのだ。
そこまでやるかと思うほどの徹底ぶり。