For example, in the following test the ReservationService::reserve_for method needs to return a duck that responds to the same methods that are defined on the (not shown) Reservation class. However, the focus of the test has nothing to do with the behavior of the reservation duck, so stub_everything is the perfect duck because it requires no additional set up and responds to all the methods required without error.
# implementation
end
# implementation
end
# implementation
end
reservation = ReservationService.reserve_for(customer, self)
MaidService.notify(reservation) if reservation.tomorrow?
VipService.notify(reservation) if reservation.for_vip?
reservation.confirmation_number
end
end
room = HotelRoom.new
ReservationService.expects(:reserve_for).with(:customer, room).returns(stub_everything)
room.book_for(:customer)
end
end
The stub_everything method is also nice because it can take parameters. For example you may want to test that the book_for method returns a confirmation number. The following test specifies the value of the confirmation_number value, but it uses stub_everything so that each other method call will return nil (without causing an error).
# implementation
end
# implementation
end
# implementation
end
reservation = ReservationService.reserve_for(customer, self)
MaidService.notify(reservation) if reservation.tomorrow?
VipService.notify(reservation) if reservation.for_vip?
reservation.confirmation_number
end
end
reservation = stub_everything(:confirmation_number => "confirmation number")
ReservationService.stubs(:reserve_for).returns(reservation)
assert_equal "confirmation number", HotelRoom.new.book_for(:customer)
end
end
Another usage of stub_everything that might not be immediately apparent is that it can be used as both a stub and a mock. The following test creates a stub and then sets an expectation on it. As a result, the expectation is verified (which is the focus of the test) and all other calls to the same object simply do nothing.
hotel = stub_everything
hotel.expects(:name=).with "Marriott"
Hotel.stubs(:new).returns hotel
Hotel.parse("Marriott||")
end
end
hotel = self.new
hotel.name, hotel.location, hotel.capacity = attributes.split("|")
hotel
end
end
Using the stub_everything method of mocha can remove noise from tests and also make them more robust by ignoring all messages that are unimportant to the current test.
Nice post Jay. I was actually wishing for something like this recently, as I'd have lots of lines that were just
ReplyDeletea.stubs(:method)
obviously not caring about its return value. This will clean things up nicely. Another great example of how its usually worth learning as much as you can about library rather than only a few of its methods.
My favorite from-scratch implementation of stub_everything...
ReplyDeleteclass Sponge
def method_missing(*args)
self
end
end
#:-)
What is also nice is stub_everything takes a block. It doesn't stub chained methods so this:
ReplyDeleteMyClass.is.awesome
Will not stub awesome if stub_everything is called like so MyClass.stubs(:is).returns(stub_everything)
So this solves it:
my_stub = stub_everything do |s|
s.stubs(:awesome).returns(whatever)
end
MyClass.stubs(:is).returns(my_stub)