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.

Marcel dos Santos

August 30, 2021
Tweet

More Decks by Marcel dos Santos

Other Decks in Programming

Transcript

  1. 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
  2. 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! 🤩
  3. sempre que nos deparamos com conteúdos sobre testes imaginamos que

    a prática será fácil de ser aplicada em um projeto real
  4. basta "apenas" preparar o cenário de teste, executar o que

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

    float { return $a + $b; } / / other implementations . . . }
  6. / / 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 } }
  7. / / 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
  8. pode-se reduzir o acoplamento através da injeção de dependências, isto

    é, fornecer uma dependência do mundo externo para uma classe
  9. / / dependency injection via constructor 
 class Car {

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


    $engine = new Engine(); $car = new Car($engine); $car - > start(); / / Starting the engine
  11. ao testar código é comum reduzir o escopo de teste

    para algo mais especí fi co, isto é, uma classe, módulo ou método
  12. 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)
  13. 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
  14. saber identi fi car o papel de cada elemento do

    código no contexto do teste ajuda numa melhor compreensão dos conceitos
  15. 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
  16. 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
  17. o conceito de dublês de teste foi criado por Gerard

    Meszaros no livro xUnit Test patterns: Refactoring Test Code
  18. o conceito de dublês de teste é simples 
 mas

    a quantidade de nomenclaturas e ferramentas acabam causando confusão
  19. 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
  20. 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
  21. um stub é utilizado para fornecer respostas fi xas ou

    pré-con fi guradas para substituir a implementação real de uma dependência
  22. 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
  23. 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
  24. 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
  25. os fakes são bastante utilizados em testes que estão na

    fronteira de I/O do sistema como, por exemplo, banco de dados
  26. 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
  27. um mock é utilizado para veri fi car interações entre

    o código testado (SUT) 
 e suas dependências (DOC)
  28. utiliza-se mocks quando o comportamento a ser testado não retorna

    nenhum valor como, por exemplo, o envio de um e-mail
  29. 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
  30. 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
  31. 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
  32. 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
  33. isso se deve pois o termo é generalizado pela maioria

    das ferramentas que criam dublês de teste
  34. os mocks surgiram como uma ferramenta 
 de design para

    descobrir a API dos colabo- radores do ponto de vista de quem vai consumi-las
  35. Spy

  36. 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
  37. 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
  38. os testes automatizados são fundamentais para a construção de um

    software robusto, ajuda na qualidade interna do código…
  39. 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
  40. o baixo acoplamento e a injeção de depen- dência permite

    a substituição de dependên- cias reais por dependências simuladas
  41. 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
  42. a maioria das pessoas se referem aos dublês de testes

    como mocks mas, como dito, existem diversos dublês diferentes