For example, assume you have an Employee class that contains check_email behavior.
class EmployeeIn the above example Employee depends on cell_phone and laptop. When testing check_email you should have (at least) two tests, one for the case where @laptop.working? is true and one where it returns false. The second scenario (where @laptop.working? is false) is perfect for stub usage.
def initialize(cell_phone, laptop)
@cell_phone = cell_phone
@laptop = laptop
end
def check_email(user, password)
if @laptop.working?
@laptop.check_email(user, password)
else
@cell_phone.check_email(user, password)
end
end
end
class EmployeeTest < Test::Unit::TestCaseThe above test is correct; however, it can be slightly changed. Because of duck typing and the fact that stubs should always return a constant value it is possible to use class methods instead of instance methods. This relieves you from needing a new instance for each stub.
class StubLaptop
def working?
false
end
end
...
def test_when_laptop_is_not_working_cell_phone_is_used
# code for setting up a mock for cell_phone
employee = Employee.new(mock_cell_phone, StubLaptop.new)
employee.check_email('jay', 'password')
# code to verify mock
end
end
class EmployeeTest < Test::Unit::TestCase
class StubLaptop
def self.working?
false
end
end
...
def test_when_laptop_is_not_working_cell_phone_is_used
# code for setting up a mock for cell_phone
employee = Employee.new(mock_cell_phone, StubLaptop)
employee.check_email('jay', 'password')
# code to verify mock
end
end
Subtle and beautiful. Great stuff Jay.
ReplyDeleteThanks Dave. Too bad you took a new gig, we have plenty of ruby work to go around. ;)
ReplyDeleteI didn't leave for lack of Ruby work. :-)
ReplyDelete