Test Doubles
Rubén Bernárdez
 www.rubenbp.com
   @rubenbpv
    #aos2011
¿Por qué usamos dobles?
El colaborador:
class Communicator():

    def send_email(self, address, total_savings):
        suponemos_que_esto_envia_un_email = True



El SUT (System Under Test):
class SavingsService():

    def __init__(self, communicator):
        self.communicator = communicator

    def analyze_month(self, month_savings):
        calculation = month_savings[0] + month_savings[1]
        self.communicator.send_email('savings@iexpertos.com', calculation)
Pruébalo cobarde! … pecador!...
class Communicator():

    def send_email(self, address, total_savings):
        suponemos_que_esto_envia_un_email = True

class SavingsService():

    def __init__(self, communicator):
        self.communicator = communicator

    def analyze_month(self, month_savings):
        calculation = month_savings[0] + month_savings[1]
        self.communicator.send_email('savings@iexpertos.com', calculation)

class LoFlipoEnEl_AOS(unittest.TestCase):
    def test_report_correcto_es_enviado_a_la_central(self):
        communicator = spy(Communicator())
        month_savings = [1234,1276435]
        service = SavingsService(communicator)

        service.analyze_month(month_savings)
        assert_that(communicator.send_email).was_called()
¿Mock o Spy?
class LoFlipoEnEl_AOS(unittest.TestCase):
    def test_al_menos_es_enviado_a_la_central(self):
        communicator = spy(Communicator())
        month_savings = [1234,1276435]
        service = SavingsService(communicator)

       service.analyze_month(month_savings)

       assert_that(communicator.send_email).was_called()

   def test_se_envia_a_la_central_y_nada_mais(self):
       communicator = mock(Communicator())
       expect_call(communicator.send_email)
       month_savings = [1234,1276435]
       service = SavingsService(communicator)

       service.analyze_month(month_savings)

       communicator.assert_that_is_satisfied()
¿Qué test se romperá?
class Communicator():

    def send_email(self, address, total_savings):
        suponemos_que_esto_envia_un_email = True

    def post_savings(self, total_savings):
        suponemos_que_esto_llama_a_un_webservice = True

class SavingsService():

    def __init__(self, communicator):
        self.communicator = communicator

    def analyze_month(self, month_savings):
        calculation = month_savings[0] + month_savings[1]
        self.communicator.send_email('savings@iexpertos.com', calculation)
        self.communicator.post_savings(calculation)
Un diseño distinto...                 ¿Ahora qué hacemos?

class Communicator():

    def send_email(self, address, total_savings):
        suponemos_que_esto_envia_un_email = True

class AccountRepository():
    def month_savings(self):
        suponemos_que_esto_devuelve_de_bd_los_ahorros = True

class SavingsService():

    def __init__(self, communicator, repository):
        self.communicator = communicator
        self.repository = repository

    def analyze_month(self):
        month_savings = self.repository.month_savings()
        calculation = month_savings[0] + month_savings[1]
        self.communicator.send_email('savings@iexpertos.com', calculation)
Mejor Stub que Spy esta vez! fistro!
class SavingsService():

    def __init__(self, communicator, repository):
        self.communicator = communicator
        self.repository = repository

    def analyze_month(self):
        month_savings = self.repository.month_savings()
        calculation = month_savings[0] + month_savings[1]
        self.communicator.send_email('savings@iexpertos.com', calculation)


class LoFlipoEnEl_AOS(unittest.TestCase):
    def test_si_hay_report_envialo_a_la_central(self):
        communicator = spy(Communicator())
        repository = stub(AccountRepository())
        when(repository.month_savings).then_return([1234,1276435])
        service = SavingsService(communicator, repository)

        service.analyze_month(month_savings)

        assert_that(communicator.send_email).was_called()
Tests frágiles VS legibilidad
    Classic VS Mockist
Test doubles frameworks
   ¿Qué más comportamientos se pueden
    programar en los dobles de test? Con qué
    frameworks?
                pyDoubles
                Mockito
                Jmock
                Moq
                Rhino.Mocks
                PHPUnit
                GoogleMock
pyDoubles.org
        Made with TDD and open source!




   when(obj.method).with_args(1).then_return(1000)
   when(obj.method).then_raise(ApplicationException())
   when(obj.method).then_return(1000)
   expect_call(obj.method).with_args(1)
   expect_call(obj.method).with_args(1, ANY_ARG)
   assert_that(obj.method).was_called().with_args(1)
   when(obj.method).with_args(
              str_containing(”abc”)).then_return(1)


                                  Powered by...
iExpertos.com
   Formación para equipos de desarrollo:
       Iniciación a TDD y TDD Avanzado
       eXtreme Programming a fondo (XP)
       Coaching

   Desarrollamos contigo:
       Dos iExpertos en tu empresa desde el arranque
        del proyecto.
       Formación y desarrollo, todo en uno.

Test Doubles - stubs, spies & mocks

  • 1.
    Test Doubles Rubén Bernárdez www.rubenbp.com @rubenbpv #aos2011
  • 2.
    ¿Por qué usamosdobles? El colaborador: class Communicator(): def send_email(self, address, total_savings): suponemos_que_esto_envia_un_email = True El SUT (System Under Test): class SavingsService(): def __init__(self, communicator): self.communicator = communicator def analyze_month(self, month_savings): calculation = month_savings[0] + month_savings[1] self.communicator.send_email('savings@iexpertos.com', calculation)
  • 3.
    Pruébalo cobarde! …pecador!... class Communicator(): def send_email(self, address, total_savings): suponemos_que_esto_envia_un_email = True class SavingsService(): def __init__(self, communicator): self.communicator = communicator def analyze_month(self, month_savings): calculation = month_savings[0] + month_savings[1] self.communicator.send_email('savings@iexpertos.com', calculation) class LoFlipoEnEl_AOS(unittest.TestCase): def test_report_correcto_es_enviado_a_la_central(self): communicator = spy(Communicator()) month_savings = [1234,1276435] service = SavingsService(communicator) service.analyze_month(month_savings) assert_that(communicator.send_email).was_called()
  • 4.
    ¿Mock o Spy? classLoFlipoEnEl_AOS(unittest.TestCase): def test_al_menos_es_enviado_a_la_central(self): communicator = spy(Communicator()) month_savings = [1234,1276435] service = SavingsService(communicator) service.analyze_month(month_savings) assert_that(communicator.send_email).was_called() def test_se_envia_a_la_central_y_nada_mais(self): communicator = mock(Communicator()) expect_call(communicator.send_email) month_savings = [1234,1276435] service = SavingsService(communicator) service.analyze_month(month_savings) communicator.assert_that_is_satisfied()
  • 5.
    ¿Qué test seromperá? class Communicator(): def send_email(self, address, total_savings): suponemos_que_esto_envia_un_email = True def post_savings(self, total_savings): suponemos_que_esto_llama_a_un_webservice = True class SavingsService(): def __init__(self, communicator): self.communicator = communicator def analyze_month(self, month_savings): calculation = month_savings[0] + month_savings[1] self.communicator.send_email('savings@iexpertos.com', calculation) self.communicator.post_savings(calculation)
  • 6.
    Un diseño distinto... ¿Ahora qué hacemos? class Communicator(): def send_email(self, address, total_savings): suponemos_que_esto_envia_un_email = True class AccountRepository(): def month_savings(self): suponemos_que_esto_devuelve_de_bd_los_ahorros = True class SavingsService(): def __init__(self, communicator, repository): self.communicator = communicator self.repository = repository def analyze_month(self): month_savings = self.repository.month_savings() calculation = month_savings[0] + month_savings[1] self.communicator.send_email('savings@iexpertos.com', calculation)
  • 7.
    Mejor Stub queSpy esta vez! fistro! class SavingsService(): def __init__(self, communicator, repository): self.communicator = communicator self.repository = repository def analyze_month(self): month_savings = self.repository.month_savings() calculation = month_savings[0] + month_savings[1] self.communicator.send_email('savings@iexpertos.com', calculation) class LoFlipoEnEl_AOS(unittest.TestCase): def test_si_hay_report_envialo_a_la_central(self): communicator = spy(Communicator()) repository = stub(AccountRepository()) when(repository.month_savings).then_return([1234,1276435]) service = SavingsService(communicator, repository) service.analyze_month(month_savings) assert_that(communicator.send_email).was_called()
  • 8.
    Tests frágiles VSlegibilidad Classic VS Mockist
  • 9.
    Test doubles frameworks  ¿Qué más comportamientos se pueden programar en los dobles de test? Con qué frameworks?  pyDoubles  Mockito  Jmock  Moq  Rhino.Mocks  PHPUnit  GoogleMock
  • 10.
    pyDoubles.org Made with TDD and open source!  when(obj.method).with_args(1).then_return(1000)  when(obj.method).then_raise(ApplicationException())  when(obj.method).then_return(1000)  expect_call(obj.method).with_args(1)  expect_call(obj.method).with_args(1, ANY_ARG)  assert_that(obj.method).was_called().with_args(1)  when(obj.method).with_args( str_containing(”abc”)).then_return(1) Powered by...
  • 11.
    iExpertos.com  Formación para equipos de desarrollo:  Iniciación a TDD y TDD Avanzado  eXtreme Programming a fondo (XP)  Coaching  Desarrollamos contigo:  Dos iExpertos en tu empresa desde el arranque del proyecto.  Formación y desarrollo, todo en uno.