$30 off During Our Annual Pro Sale. View Details »

Desmistificando os Dublês de Testes

Desmistificando os Dublês de Testes

Um dos conceitos que mais causam confusão durante o aprendizado de testes de software são os dublês de teste. Eles são o próximo passo nos estudos após uma pessoa ser introduzida aos testes automatizados. Nesta palestra apresentarei o que são os dublês de teste, quais são os tipos de dublês mais conhecidos (dummy, stub, fake, mock e spy), quais problemas eles resolvem e quais são as diferenças entre eles. Após essa palestra você terá um entendimento sólido sobre o tema e terá mais segurança ao realizar os seus testes.

Marcel dos Santos

August 30, 2021
Tweet

More Decks by Marcel dos Santos

Other Decks in Programming

Transcript

  1. Marcel Gonçalves dos Santos
    @marcelgsantos
    dublês de testes
    desmistificando os

    View Slide

  2. pensandonaweb.com.br
    desenvolvedor web full-stack
    Marcel Gonçalves dos Santos
    @marcelgsantos

    View Slide

  3. @phpsp
    phpsp.org.br

    View Slide

  4. Interaja nas mídias sociais!


    - fale sobre o evento, palestrantes e conteúdo


    - tire fotos do evento e publique

    - interaja com outros participantes do evento


    - tire dúvidas ou dê feedbacks para os palestrantes

    View Slide

  5. 1. seguir @marcelgsantos no Twitter

    2. tuitar utilizando as hashtags #testes e
    #TheDevConf e me marcar no tweet

    3. não vale tuíte em branco

    4. não vale retuíte
    Concorra a um livro da Casa do Código!
    🤩

    View Slide

  6. Introdução

    View Slide

  7. é senso comum que testes automatizados
    são fundamentais para a construção de um
    software robusto

    View Slide

  8. sempre que nos deparamos com conteúdos
    sobre testes imaginamos que a prática será
    fácil de ser aplicada em um projeto real

    View Slide

  9. basta "apenas" preparar o cenário de teste,
    executar o que queremos testar e comparar
    o resultado com o esperado, não é mesmo?

    View Slide

  10. class Calculator


    {


    public function sum(float $a, float $b)
    :
    float


    {


    return $a + $b;


    }


    / /
    other implementations
    . . .

    }

    View Slide

  11. mas, na prática, testar uma aplicação é um
    pouco mais complicado do que parece!

    View Slide

  12. o objetivo dessa palestra é facilitar a
    compreensão de diversos conceitos sobre
    testes

    View Slide

  13. Princípios


    de Design

    View Slide

  14. coesão


    indica o grau de relação entre os membros
    de um módulo

    View Slide

  15. / /
    not cohesive class

    class Cart


    {


    private $items;


    public function
    _ _
    construct() {


    $this
    - >
    items = [];


    }


    public function numberOfItems() : int {


    return count($this
    - >
    items);


    }


    public function calculateDeliveryPrice() : float {


    / /
    calculates the delivery price


    }


    }

    View Slide

  16. acoplamento


    indica o grau de dependência entre classes

    View Slide

  17. o acoplamento ocorre quando o código de
    um módulo utiliza código…

    View Slide

  18. …de outro módulo, seja ao chamar uma
    função ou acessar algum dado

    View Slide

  19. / /
    class Car is coupled to Engine class

    class Car


    {


    private $engine;


    public function
    _ _
    construct() {


    $this
    - >
    engine = new Engine();


    }


    public function start() {


    $this
    - >
    engine
    - >
    start();
    / /
    engine is a dependency


    }


    }


    $car = new Car();


    $car
    - >
    start();
    / /
    Starting the engine

    View Slide

  20. o acoplamento é algo desejado, porém, que
    deve ser controlado

    View Slide

  21. ao controlar o acoplamento o software
    torna-se mais
    fl
    exível e fácil de manter

    View Slide

  22. pode-se reduzir o acoplamento através da
    injeção de dependências, isto é, fornecer
    uma dependência do mundo externo para
    uma classe

    View Slide

  23. /
    /
    dependency injection via constructor

    class Car


    {


    private $engine;


    public function
    _
    _
    construct(Engine $engine)


    {


    $this
    -
    >
    engine = $engine;


    }


    public function start()


    {


    $this
    -
    >
    engine
    -
    >
    start();


    }


    }

    View Slide

  24. / /
    inject an engine dependency into the car

    $engine = new Engine();


    $car = new Car($engine);


    $car
    - >
    start();
    / /
    Starting the engine

    View Slide

  25. Testes

    View Slide

  26. ao testar código é comum reduzir o escopo
    de teste para algo mais especí
    fi
    co, isto é,
    uma classe, módulo ou método

    View Slide

  27. o código que é alvo de teste é chamado
    de system under test (SUT)

    View Slide

  28. esse código, normalmente, possui
    dependências que são passadas via
    construtor

    View Slide

  29. class CreateUser


    {


    private UserRepository $userRepository;


    private Notif
    i
    cationService $notif
    i
    cationService;


    public function
    _ _
    construct(


    UserRepository $userRepository,


    Notif
    i
    cationService $notif
    i
    cationService


    ) {


    $this
    - >
    userRepository = $userRepository;


    $this
    - >
    notif
    i
    cationService = $notif
    i
    cationService;


    }


    public function execute(array $data)
    :
    void


    {

    / /
    orchestrates the domain to create a user
    . . .

    }


    }
    a classe CreateUser é


    o system under test (SUT)

    View Slide

  30. uma dependência é chamada de depended-
    on-component (DOC) ou colaborador

    View Slide

  31. class CreateUser


    {


    private UserRepository $userRepository;


    private Notif
    i
    cationService $notif
    i
    cationService;


    public function
    _ _
    construct(


    UserRepository $userRepository,


    Notif
    i
    cationService $notif
    i
    cationService


    ) {


    $this
    - >
    userRepository = $userRepository;


    $this
    - >
    notif
    i
    cationService = $notif
    i
    cationService;


    }


    public function execute(array $data)
    :
    void


    {

    / /
    orchestrates the domain to create a user
    . . .

    }


    }
    UserRepository e NotificationService


    são colaboradores (DOC), isto é, poderão
    ser substituídos por dublês de testes

    View Slide

  32. saber identi
    fi
    car o papel de cada elemento
    do código no contexto do teste ajuda numa
    melhor compreensão dos conceitos

    View Slide

  33. Dublês de Testes

    View Slide

  34. os dublês de teste são substitutos que
    sobrepõem dependências necessárias para
    se testar um sistema ou um comportamento

    View Slide

  35. os dublês de teste são melhor aplicados
    quando substituem dependências lentas,
    não-determinísticas ou de difícil
    con
    fi
    guração

    View Slide

  36. são exemplos de dependências lentas as
    chamadas para APIs e não-determinísticas
    um serviço que obtém datas ou gera
    números aleatórios

    View Slide

  37. o conceito de dublês de teste foi criado
    por Gerard Meszaros no livro xUnit Test
    patterns: Refactoring Test Code

    View Slide

  38. os dublês de teste mais conhecidos são:
    dummy, stub, spy, fake e mock

    View Slide

  39. o conceito de dublês de teste é simples

    mas a quantidade de nomenclaturas e
    ferramentas acabam causando confusão

    View Slide

  40. As Fases de Teste

    do Padrão xUnit

    View Slide

  41. as quatro fases de teste são:


    setup - coloca o sistema no estado necessário

    exercise - interage com o objeto sob teste

    verify - veri
    fi
    ca se o comportamento aconteceu

    teardown - colocar o sistema no estado
    anterior ao teste

    View Slide

  42. Dummy

    View Slide

  43. um dummy é utilizado para preencher
    parâmetros obrigatórios

    View Slide

  44. eles podem ser criados manualmente, isto
    é, sem a necessidade de ferramentas
    externas

    View Slide

  45. eles são os dublês de testes mais simples
    que existem

    View Slide

  46. public function testRemoveAnInexistentItemShouldThrowAnException()
    :
    void


    {


    $product = new Product(1, 'hamburguer', 20);


    $item = new Item($product, 2, 0);


    $shoppingCart = new ShoppingCart();


    $this
    -
    >
    expectException(ShoppingCartException
    :
    :
    class);


    $shoppingCart
    -
    >
    removeItem($item);


    } o objeto item é um dummy pois,
    apesar da necessidade de passá-lo
    como parâmetro, ele não é utilizado
    na lógica que lança a exceção

    View Slide

  47. ao ser passado como dependência ou parâ-
    metro um dummy pode não ser utilizado

    View Slide

  48. Stub

    View Slide

  49. um stub é utilizado para fornecer respostas
    fi
    xas ou pré-con
    fi
    guradas para substituir a
    implementação real de uma dependência

    View Slide

  50. eles podem ser criados manualmente ou
    utilizando ferramentas externas

    View Slide

  51. eles evitam que chamadas lentas ou não-
    determinísticas sejam feitas durante a
    execução do teste

    View Slide

  52. public function testCreateUserWithExistentEmailShouldNotWork()
    :
    void


    {


    $existentEmail = '[email protected]';


    $payload = $this
    -
    >
    validPayload();


    $userRepositoryMock = $this
    -
    >
    createMock(UserRepository
    :
    :
    class);


    $notif
    i
    cationServiceMock = $this
    -
    >
    createMock(Notif
    i
    cationService
    :
    :
    class);


    $createUser = new UserCreator($userRepositoryMock, $notif
    i
    cationServiceMock);


    $userRepositoryMock


    -
    >
    method('f
    i
    ndIfEmailIsUsedByUser')


    -
    >
    with($existentEmail)


    -
    >
    willReturn(true);


    $this
    -
    >
    expectException(InvalidUserException
    :
    :
    class);


    $this
    -
    >
    expectExceptionMessage("O e
    -
    mail $existentEmail já está sendo utilizado.");


    $createUser
    -
    >
    execute($payload);


    }
    cria uma resposta pré-configurada
    para colocar o código no estado
    esperado para a realização do teste
    apesar de utilizar o método createMock,
    o dublê de teste trata-se de um stub

    View Slide

  53. deve-se utilizar stubs quando precisar de
    respostas rápidas, determinísticas e pré-
    con
    fi
    guradas para o seu teste

    View Slide

  54. um stub é um dublê de teste utilizado
    na fase de setup

    View Slide

  55. Fake

    View Slide

  56. um fake é utilizado para fornecer respostas
    fi
    xas ou pré-con
    fi
    guradas, assim como um
    stub, com a diferença que ele utiliza uma
    implementação funcional

    View Slide

  57. um fake não precisa conhecer a
    implementação concreta de quem ele
    substitui, isto é, não ocorre vazamento de
    detalhes internos das dependências

    View Slide

  58. ele pode ser utilizado para reproduzir
    respostas mais complexas que não seria
    possível utilizando stubs

    View Slide

  59. os fakes são bastante utilizados em testes
    que estão na fronteira de I/O do sistema
    como, por exemplo, banco de dados

    View Slide

  60. class MemoryCustomerRepository implements CustomerRepository


    {


    private array $customers;


    public function persist(Customer $customer)
    :
    void {


    $this
    -
    >
    customers[$customer
    -
    >
    id()] = $customer;


    }


    public function f
    i
    ndById(CustomerId $customerId)
    :
    Customer {


    return $this
    -
    >
    customers[$customerId];


    }


    public function f
    i
    ndByBirthday(DateTimeImmutable $currentDate)
    :
    array {


    return array_f
    i
    lter(


    $this
    -
    >
    customers,


    fn(Customer $customer)
    =
    >
    $customer
    -
    >
    isBirthday($currentDate)


    );


    }


    }
    um fake, diferentemente de um stub,
    possui uma implementação funcional

    View Slide

  61. Mock

    View Slide

  62. um mock é utilizado para veri
    fi
    car
    interações entre o código testado (SUT)

    e suas dependências (DOC)

    View Slide

  63. os objetos se comunicam através de troca
    de mensagens, isto é, chamadas de métodos

    View Slide

  64. um mock é utilizado para garantir que
    a mensagem foi enviada

    View Slide

  65. utiliza-se mocks quando o comportamento a
    ser testado não retorna nenhum valor como,
    por exemplo, o envio de um e-mail

    View Slide

  66. public function testCreateUserWithValidDataShouldWork()
    :
    void


    {


    $payload = $this
    -
    >
    validPayload();


    $userRepositoryMock = $this
    -
    >
    createMock(UserRepository
    :
    :
    class);


    $notif
    i
    cationServiceMock = $this
    -
    >
    createMock(Notif
    i
    cationService
    :
    :
    class);


    $createUser = new UserCreator($userRepositoryMock, $notif
    i
    cationServiceMock);


    $userRepositoryMock


    -
    >
    expects($this
    -
    >
    once())


    -
    >
    method('persist');


    $notif
    i
    cationServiceMock


    -
    >
    expects($this
    -
    >
    once())


    -
    >
    method('sendConf
    i
    rmation');


    $createUser
    -
    >
    execute($payload);


    }
    o mock garante que o método de uma
    dependência foi chamado, isto é, que a
    mensagem foi enviada para o outro objeto
    a fase de verify é feita antes da fase de
    exercise quando utilizamos mock

    View Slide

  67. podemos dizer que o mock faz a veri
    fi
    cação
    por comportamento e os outros dublês de
    testes fazem a veri
    fi
    cação por estado

    View Slide

  68. um mock é um dublê de teste utilizado
    na fase de verify

    View Slide

  69. o termo "mockar um objeto" refere-se a
    programá-lo para veri
    fi
    car se ele recebeu a
    mensagem e não programá-lo para retornar
    um valor padrão

    View Slide

  70. quando mockar um objeto?


    - quando um objeto é difícil de ser
    instanciado


    - quando um objeto causa efeitos no mundo
    exterior (enviar e-mail)


    - quando quero veri
    fi
    car e garantir o
    comportamento de um colaborador

    View Slide

  71. todos os dublês de testes, por questões de
    simplicidade, são comumente chamados de
    mock

    View Slide

  72. isso se deve pois o termo é generalizado
    pela maioria das ferramentas que criam
    dublês de teste

    View Slide

  73. os mocks surgiram como uma ferramenta

    de design para descobrir a API dos colabo-
    radores do ponto de vista de quem vai
    consumi-las

    View Slide

  74. essa abordagem é conhecida como modo
    outside-in

    View Slide

  75. Spy

    View Slide

  76. um spy é utilizado para veri
    fi
    car interações
    entre o código testado (SUT) e suas depen-
    dências (DOC), porém, utiliza uma imple-
    mentação funcional para operar

    View Slide

  77. os spies são dublês de testes que registram
    como eles são utilizados de forma que con-
    seguimos perguntá-los sobre a execução
    e fazer asserções

    View Slide

  78. eles podem ser utilizados como uma
    alternativa ao uso de mocks

    View Slide

  79. Categorias

    View Slide

  80. os dublês de testes podem ser
    categorizados de inúmeras formas

    View Slide

  81. um dublê de teste pode simular
    comportamento ou observar interações

    View Slide

  82. um dublê de teste pode também ter ou não
    uma implementação funcional

    View Slide

  83. dummy


    - não simula comportamento e não observa
    interação


    - não possui implementação funcional

    View Slide

  84. stub


    - simula comportamento


    - não possui implementação funcional

    View Slide

  85. fake


    - simula comportamento


    - possui implementação funcional

    View Slide

  86. mock


    - observa interação


    - não possui implementação funcional

    View Slide

  87. spy


    - observa interação


    - possui implementação funcional

    View Slide

  88. Conclusão

    View Slide

  89. os testes automatizados são fundamentais
    para a construção de um software robusto,
    ajuda na qualidade interna do código…

    View Slide

  90. …e permite que o software tenha uma
    evolução sustentável

    View Slide

  91. para facilitar a construção de testes
    recomenda-se seguir boas práticas de
    design de código como ter um código
    com alta coesão e baixo acoplamento

    View Slide

  92. o baixo acoplamento e a injeção de depen-
    dência permite a substituição de dependên-
    cias reais por dependências simuladas

    View Slide

  93. essas dependências simuladas são chama-
    das de dublês de testes e permitem criar
    testes rápidos, determinísticos e de fácil
    con
    fi
    guração

    View Slide

  94. existem vários dublês de testes cada um
    com suas características e usos

    View Slide

  95. a maioria das pessoas se referem aos dublês
    de testes como mocks mas, como dito,
    existem diversos dublês diferentes

    View Slide

  96. vá em frente, estude e divirta-se! ;)

    View Slide

  97. Referências

    View Slide

  98. bit.ly/palestra-dubles-de-testes

    View Slide

  99. Avalie!

    View Slide

  100. @marcelgsantos
    speakerdeck.com/marcelgsantos
    Obrigado.
    Perguntas?

    View Slide