what are doubles?
why should we use doubles?
Unit tests must isolate the SUT.
So all collaborators should be replaced.
 collaborator = Collaborator()
 sut = SUT(collaborator)
 sut.exercise()
	    
 double = Double(Collaborator)
 sut = SUT(double)
 sut.exercise()
	    [ according to xunitpatterns.com/Test Double.html ]
  class AccountService:
      def __init__(self, store, password_service):
	  [...]
      def create_user(self, login):
          [...]
          - raise InvalidPassword()
          - raise AlreadyExists()
      def create_group(self, group_name, user_names):
          [...]
	    [ slides related to that example
have gray background ]
collaborators
  class AccountStore:
      def save(self, login, password):
          [...]
      def has_user(self, login):
          [...]
  class PasswordService:
      def generate(self):
          [...]
	    
	      In a free stub, any method may be invoked:
	    
  class AccountTests(TestCase):
      def test_account_creation(self):
          with Stub() as password_service:
              password_service.generate().returns('secret')
          service = AccountService(store=Stub(), password_service)
          service.create_user('John')
	    ... you can set return value depending on arguments
 with Stub() as stub:
     stub.foo(2, 2).returns(100)
     stub.foo(3, ANY_ARG).returns(200)
 assert_that(stub.foo(1, 1), is_(None))
 assert_that(stub.foo(2, 2), is_(100))
 assert_that(stub.foo(3, 0), is_(200))
	    ... or by hamcrest matcher
 with Stub() as stub:
     stub.foo(2, greater_than(4)).returns(100)
 assert_that(stub.foo(2, 1), is_(None))
 assert_that(stub.foo(2, 5), is_(100))
	    ... or by composite hamcrest matcher
 with Stub() as stub:
     stub.foo(2, has_length(all_of(
         greater_than(4), less_than(8)))).returns(1000)
 assert_that(stub.foo(2, "bad"), is_(None))
 assert_that(stub.foo(2, "enough"), is_(1000))
	    interface may be restricted to a given class:
 with Stub(PasswordService) as password_service:
     password_service.generate().returns('secret')
 stub.generate()
 stub.generate(9)
TypeError: PasswordService.generate() takes exactly 1 argument (2 given)
 stub.wrong()
AttributeError: 'PasswordService' object has no attribute 'wrong'
	    
  class AccountTests(TestCase):
      def test_account_creation(self):
          with Stub(PasswordService) as password_service:
              password_service.generate().returns('secret')
          service = AccountService(store=Stub(), password_service)
          service.create_user('John')
    ... is 'store' really called??
we need a spy
checking double invocations: called()
 store = Spy(AccountStore)
 service = AccountService(store, password_service)
 service.create_group('team', ['John', 'Peter', 'Alice'])
 assert_that(store.save, called())
    but... is really called three times?
checking called times: times()
(also with matchers)
 store = Spy(AccountStore)
 service = AccountService(store, password_service)
 service.create_group('team', ['John', 'Peter', 'Alice'])
 assert_that(store.save, called().times(3))
 assert_that(store.save, called().times(greater_than(2)))
    but... is really called with the right arguments?
check argument values: with_args()
(also with matchers)
 store = Spy(AccountStore)
 service = AccountService(store, password_service)
 service.create_user('John')
 assert_that(store.save, called().with_args('John', 'secret'))
 assert_that(store.save, called().with_args('John', ANY_ARG))
 assert_that(store.save,
             called().with_args(contains_string('oh'), ANY_ARG))
 assert_that(store.save,
             never(called().with_args('Alice', anything())))
    check keyword argument
(also with matcher)
 spy = Spy()
 spy.foo(name="Mary")
 assert_that(spy.foo,
             called().with_args(name="Mary"))
 assert_that(spy.foo,
             called().with_args(name=contains_string("ar")))
    meaning-full report messages!
 service.create_group('team', ['John', 'Alice'])
 assert_that(store.save, called().with_args('Peter'))
AssertionError:
Expected: these calls:
          AccountStore.save('Peter')
     but: calls that actually ocurred were:
          AccountStore.has_user('John')
          AccountStore.save('John', 'secret')
          AccountStore.has_user('Alice')
          AccountStore.save('Alice', 'secret')
    propagates invocations to the collaborator
 with ProxySpy(AccountStore()) as store:
     store.has_user('John').returns(True)
 service = AccountService(store, password_service)
 with self.assertRaises(AlreadyExists):
     service.create_user('John')
    
      CAUTION: ProxySpy is not a true double,
this invokes the actual AccountStore instance!
    
programming expectations
        with Mock(AccountStore) as store:
            store.has_user('John')
            store.save('John', anything())
            store.has_user('Peter')
            store.save('Peter', anything())
        service = AccountService(store, password_service)
	service.create_group('team', ['John', 'Peter'])
        assert_that(store, verify())
    Mock assures these invocations (and only these) are ocurred.
 collaborator = Collaborator()
 collaborator.foo = method_returning('bye')
 assert_that(self.collaborator.foo(), is_('bye'))
 collaborator.foo = method_raising(SomeException)
 collaborator.foo()
SomeException:
  attach additional behavior
 class Observer(object):
     def __init__(self):
         self.state = None
     def update(self, *args, **kargs):
         self.state = args[0]
 observer = Observer()
 stub = Stub()
 stub.foo.attach(observer.update)
 stub.foo(2)
 assert_that(observer.state, is_(2))
  delegating to callables
 def get_pass():
     return "12345"
 with Stub(PasswordService) as password_service:
     password_service.generate().delegates(get_pass)
 store = Spy(AccountStore)
 service = AccountService(store, password_service)
 service.create_user('John')
 assert_that(store.save, called().with_args('John', '12345'))
    delegating to iterables/generators
 with Stub(PasswordService) as password_service:
     password_service.generate().delegates(["12345", "mypass", "nope"])
 store = Spy(AccountStore)
 service = AccountService(store, password_service)
 service.create_group('team', ['John', 'Peter', 'Alice'])
 assert_that(store.save, called().with_args('John', '12345'))
 assert_that(store.save, called().with_args('Peter', 'mypass'))
 assert_that(store.save, called().with_args('Alice', 'nope'))
    
 class Collaborator(object):
     @property
     def prop(self):
         return 1
     @prop.setter
     def prop(self, value):
         pass
 with Spy(Collaborator) as spy:
     spy.prop = 2
 assert_that(spy.prop, is_(2))  # double property getter invoked
    (also with matcher)
 assert_that(spy, property_got('prop'))
 spy.prop = 4  # double property setter invoked
 spy.prop = 5  # --
 spy.prop = 5  # --
 assert_that(spy, property_set('prop'))  # set to any value
 assert_that(spy, property_set('prop').to(4))
 assert_that(spy, property_set('prop').to(5).times(2))
 assert_that(spy,
             never(property_set('prop').to(greater_than(6))))