Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

52711e2157a6fed933b0361cc06a6953?s=128

Marcel dos Santos

August 30, 2021
Tweet

Transcript

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

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

  3. @phpsp phpsp.org.br

  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
  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! 🤩
  6. Introdução

  7. é senso comum que testes automatizados são fundamentais para a

    construção de um software robusto
  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
  9. basta "apenas" preparar o cenário de teste, executar o que

    queremos testar e comparar o resultado com o esperado, não é mesmo?
  10. class Calculator { public function sum(float $a, float $b) :

    float { return $a + $b; } / / other implementations . . . }
  11. mas, na prática, testar uma aplicação é um pouco mais

    complicado do que parece!
  12. o objetivo dessa palestra é facilitar a compreensão de diversos

    conceitos sobre testes
  13. Princípios de Design

  14. coesão indica o grau de relação entre os membros de

    um módulo
  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 } }
  16. acoplamento indica o grau de dependência entre classes

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

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

    algum dado
  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
  20. o acoplamento é algo desejado, porém, que deve ser controlado

  21. ao controlar o acoplamento o software torna-se mais fl exível

    e fácil de manter
  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
  23. / / dependency injection via constructor 
 class Car {

    private $engine; public function _ _ construct(Engine $engine) { $this - > engine = $engine; } public function start() { $this - > engine - > start(); } }
  24. / / inject an engine dependency into the car 


    $engine = new Engine(); $car = new Car($engine); $car - > start(); / / Starting the engine
  25. Testes

  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
  27. o código que é alvo de teste é chamado de

    system under test (SUT)
  28. esse código, normalmente, possui dependências que são passadas via construtor

  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)
  30. uma dependência é chamada de depended- on-component (DOC) ou colaborador

  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
  32. saber identi fi car o papel de cada elemento do

    código no contexto do teste ajuda numa melhor compreensão dos conceitos
  33. Dublês de Testes

  34. os dublês de teste são substitutos que sobrepõem dependências necessárias

    para se testar um sistema ou um comportamento
  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
  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
  37. o conceito de dublês de teste foi criado por Gerard

    Meszaros no livro xUnit Test patterns: Refactoring Test Code
  38. os dublês de teste mais conhecidos são: dummy, stub, spy,

    fake e mock
  39. o conceito de dublês de teste é simples 
 mas

    a quantidade de nomenclaturas e ferramentas acabam causando confusão
  40. As Fases de Teste 
 do Padrão xUnit

  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
  42. Dummy

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

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

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

  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
  47. ao ser passado como dependência ou parâ- metro um dummy

    pode não ser utilizado
  48. Stub

  49. um stub é utilizado para fornecer respostas fi xas ou

    pré-con fi guradas para substituir a implementação real de uma dependência
  50. eles podem ser criados manualmente ou utilizando ferramentas externas

  51. eles evitam que chamadas lentas ou não- determinísticas sejam feitas

    durante a execução do teste
  52. public function testCreateUserWithExistentEmailShouldNotWork() : void { $existentEmail = 'joao.silva@example.com'; $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
  53. deve-se utilizar stubs quando precisar de respostas rápidas, determinísticas e

    pré- con fi guradas para o seu teste
  54. um stub é um dublê de teste utilizado na fase

    de setup
  55. Fake

  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
  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
  58. ele pode ser utilizado para reproduzir respostas mais complexas que

    não seria possível utilizando stubs
  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
  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
  61. Mock

  62. um mock é utilizado para veri fi car interações entre

    o código testado (SUT) 
 e suas dependências (DOC)
  63. os objetos se comunicam através de troca de mensagens, isto

    é, chamadas de métodos
  64. um mock é utilizado para garantir que a mensagem foi

    enviada
  65. utiliza-se mocks quando o comportamento a ser testado não retorna

    nenhum valor como, por exemplo, o envio de um e-mail
  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
  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
  68. um mock é um dublê de teste utilizado na fase

    de verify
  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
  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
  71. todos os dublês de testes, por questões de simplicidade, são

    comumente chamados de mock
  72. isso se deve pois o termo é generalizado pela maioria

    das ferramentas que criam dublês de teste
  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
  74. essa abordagem é conhecida como modo outside-in

  75. Spy

  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
  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
  78. eles podem ser utilizados como uma alternativa ao uso de

    mocks
  79. Categorias

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

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

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

    implementação funcional
  83. dummy - não simula comportamento e não observa interação -

    não possui implementação funcional
  84. stub - simula comportamento - não possui implementação funcional

  85. fake - simula comportamento - possui implementação funcional

  86. mock - observa interação - não possui implementação funcional

  87. spy - observa interação - possui implementação funcional

  88. Conclusão

  89. os testes automatizados são fundamentais para a construção de um

    software robusto, ajuda na qualidade interna do código…
  90. …e permite que o software tenha uma evolução sustentável

  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
  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
  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
  94. existem vários dublês de testes cada um com suas características

    e usos
  95. a maioria das pessoas se referem aos dublês de testes

    como mocks mas, como dito, existem diversos dublês diferentes
  96. vá em frente, estude e divirta-se! ;)

  97. Referências

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

  99. Avalie!

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