Feature: Return value contracts In order to be able to record the stubbed value, Bogus requires that the block you pass when stubbing is free of side effects and idempotent. If it's not, the behavior of contract verification is not defined. Background: Given a file named "session_controller.rb" with: """ruby class SessionController def initialize(authentication_service) @authentication_service = authentication_service end def create(params) @authentication_service.authenticate(params[:login], params[:password]) :render_welcome rescue :render_error end end """ Given a file named "authentication_service.rb" with: """ruby class AuthenticationService def initialize(user_db) @user_db = user_db end def authenticate(login, password) unless @user_db[login] == Digest::SHA1.hexdigest(password) raise WrongPassword end end end class WrongPassword < StandardError end """ And a spec file named "session_controller_spec.rb" with: """ruby require_relative 'session_controller' require_relative 'authentication_service' describe SessionController do fake(:authentication_service) let(:controller) { SessionController.new(authentication_service) } it "logs in the user with valid data" do stub(authentication_service).authenticate('foo', 'bar') expect(controller.create(login: 'foo', password: 'bar')).to eq(:render_welcome) end it "fails with invalid data" do stub(authentication_service).authenticate('baz', 'bar') { raise WrongPassword } expect(controller.create(login: 'baz', password: 'bar')).to eq(:render_error) end end """ Scenario: Bogus makes sure that all the return values recorded by stubbing are also present in tests of the real object Then spec file with following content should fail: """ruby require_relative 'authentication_service' describe AuthenticationService do verify_contract(:authentication_service) it "logs in the user" do service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar')) expect { service.authenticate('foo', 'bar') }.not_to raise_error end end """ Scenario: Bogus does not fail the tests if all the recorded values have been also recorded on the real object Then spec file with following content should pass: """ruby require_relative 'authentication_service' describe AuthenticationService do verify_contract(:authentication_service) it "logs in the user" do service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar')) expect { service.authenticate('foo', 'bar') }.not_to raise_error end it "raises WrongPassword with incorrect credentials" do service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar')) expect { service.authenticate('baz', 'bar') }.to raise_error(WrongPassword) end end """