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

Uma breve introdução aos testes automatizados

Uma breve introdução aos testes automatizados

Não tão breve

Oscar Esgalha

May 17, 2018
Tweet

More Decks by Oscar Esgalha

Other Decks in Technology

Transcript

  1. 1. Teste Automatizado 2. Caixa branca e caixa preta 3.

    Estático e Dinâmico 4. Teste de Fumaça 5. Teste unitário 6. Caminho (In)feliz 7. Mocks 8. Fixtures e Factories 9. Teste de Regressão 10. Teste de Integração 11. Performance 12. Cobertura 13. Mutantes 14. Teste de Propriedade
  2. Teste Automatizado Um programa que recebe como entrada outro programa

    e deve responder na saída algumas perguntas sobre a qualidade do programa testado. Exemplo dessas perguntas: Funciona de acordo com a especificação? Consegue lidar com exceções? Executa em tempo razoável?
  3. Teste Automatizado Vantagens Essencial para entrega contínua Muitas opções de

    ferramentas e frameworks Pode prevenir bugs Pode revelar inconsistências na especificação Pode melhorar a sua habilidade de escrever software
  4. Teste Automatizado Desvantagens Pode atrasar o desenvolvimento Pode atrasar a

    execução do pipeline Pode trazer uma falsa sensação de confiança Pode piorar a sua habilidade de escrever software
  5. Caixa branca e caixa preta Caixa Branca Testes num software

    com acesso ao código fonte Caixa Preta Testes num software sem acesso ao código fonte
  6. Estático e Dinâmico Estático Avalia a qualidade de um software

    sem executá‑lo Dinâmico Avalia a qualidade de um software executando‑o
  7. Teste de Fumaça Responde perguntas básicas: O compilador consegue compilar

    o código fonte? O interpretador consegue interpretar o código fonte? O processo fica em execução? O servidor está escutando na porta 8002? A homepage do website abre?
  8. Teste de Fumaça O objetivo é falhar rápido para evitar

    a execução de testes mais sofisticados e demorados. Pode ser usado quando o software avaliado não tem nenhum tipo de teste, pois é uma forma rápida e barata de evitar o deploy de software completamente quebrado.
  9. Teste Unitário Estamos gastando muito dinheiro enviando e‑mail de marketing

    para usuários com domínio de e‑mail "falso". Precisamos validar no cadastro se o domínio de e‑mail do usuário está numa lista de domínios bloqueados, em caso positivo devemos bloquear esse cadastro. Implementar uma função para validar um endereço de e‑mail.
  10. Teste Unitário BLOCKED_DOMAINS = [...] def valid_mail(mail_address): _, domain =

    mail_address.split('@') return domain not in BLOCKED_DOMAINS
  11. Teste Unitário import unittest from validators import valid_mail class TestMailValidator(unittest.TestCase):

    def test_accept_valid_mail(self): self.assertTrue(valid_mail('[email protected]')) def test_reject_spam_mail(self): self.assertFalse(valid_mail('[email protected]'))
  12. Teste Unitário Um usuário digitou o e‑mail errado e deu

    erro 500 no site! O shift do teclado do usuário está ruim e na hora de digitar o e‑mail saiu "gilberto2gmail.com" ao invés de "[email protected]"
  13. Caminho (In)feliz Um caminho feliz (happy path) é um cenário

    de execução em que não ocorre nada fora do esperado, de modo que uma rotina siga até o final sem a ocorrência de nenhuma exceção. A entrada chega no formato esperado e a interação com outros sistemas (ou até mesmo outras unidades) ocorre de modo ideal.
  14. Caminho (In)feliz Em um ambiente produtivo ocorrerão desvios do caminho

    feliz. Podemos escrever código para lidar com exceções e testar se a unidade lida bem com as exceções.
  15. Caminho (In)feliz BLOCKED_DOMAINS = [...] def valid_mail(mail_address): if not isinstance(mail_address,

    str): return False if '@' not in mail_address: return False _, domain = mail_address.split('@') return domain not in BLOCKED_DOMAINS
  16. Caminho (In)feliz class TestMailValidator(unittest.TestCase): def test_accept_valid_mail(self): self.assertTrue(valid_mail('[email protected]')) def test_reject_spam_mail(self): self.assertFalse(valid_mail('[email protected]'))

    def test_reject_invalid_args(self): self.assertFalse(valid_mail('gilberto2gmail.com')) self.assertFalse(valid_mail('textolixo')) self.assertFalse(valid_mail(2018))
  17. Mocks Mocks, stubs, fakes e test doubles são ferramentas usadas

    para testar uma unidade que precisa interagir com outras unidades. O objetivo dessas ferramentas é evitar a necessidade de uma interação, imitando o comportamento da outra unidade: recebe os mesmos parâmetros e produz uma saída conhecida.
  18. I think the lack of reusability comes in object‑oriented languages,

    not functional languages. Because the problem with object‑ oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. Joe Armstrong Mock te dá o poder de construir uma selva virtual.
  19. Mocks Nós reduzimos os gastos com o e‑mail de marketing,

    mas ainda estamos recebendo usuários com e‑mail de spam. Parece que serviços desse tipo surgem com frequência então precisamos usar uma lista atualizada. Assinamos um serviço pra isso, a nossa validação de e‑mail agora deve consultar a lista de bloqueio dessa API: https://spamminator.it/api/v1/domains
  20. Mocks import requests SPAMAPI_URL = 'https://spamminator.it/api/v1/domains' def _blocked_domains(): return requests.get(SPAMAPI_URL).json()

    def valid_mail(mail_address): if not isinstance(mail_address, str): return False if '@' not in mail_address: return False _, domain = mail_address.split('@') return domain not in _blocked_domains()
  21. Mocks A equipe reclamou que o Jenkins ficou mais lento

    e estamos fazendo tantas requisições por dia na API do Spamminator™ que quase não compensou a economia de não enviar e‑mails para endereços de spam!
  22. Mocks from unittest import TestCase from unittest.mock import patch from

    validators import valid_mail class TestMailValidator(TestCase): @patch('validators._blocked_domains', return_value=['mailinat def test_reject_spam_mail(self, _mocked_method): self.assertFalse(valid_mail('[email protected]'))
  23. Mocks Aceleram a velocidade dos testes evitando a necessidade de

    interagir com alguma rotina custosa. Facilita a validação de resultados pois permite manipulá‑los. É possível checar se e quantas vezes o código chamou a unidade mockada.
  24. Passo a passo para escrever um caso de teste para

    qualquer banana: 1. Construir a selva digital (contextualizar) 2. Interagir com a banana (executar o código) 3. Validar o estado final da banana (rodar as asserções) 4. Destruir a selva (destruir o contexto)
  25. Fixtures e Factories No contexto de frameworks web fortemente atrelados

    ao banco de dados (com ORM) é comum durante a fase de contextualização de casos de teste criar várias entradas no banco de dados. Fixtures e Factories são duas ferramentas que podem facilitar esse trabalho.
  26. Fixtures e Factories Fixtures Dados estáticos (JSON, YAML) que são

    inseridos no banco de dados antes de cada caso de teste. Factories Métodos auxiliares para popular o banco de dados dinamicamente. Gera dados para serem inseridos e permite especificar alguns parâmetros.
  27. Testes de Regressão Um caso de teste adicionado junto com

    a correção de um bug para garantir que no futuro o mesmo bug não reapareça.
  28. Testes de Regressão 1. Um bug foi descoberto em produção

    ou durante testes manuais 2. Descubra a origem do bug 3. Escreva um teste para reproduzir o bug (o teste deve falhar) 4. Escreva a correção para o bug (agora o teste deve passar)
  29. Teste de Integração O foco dos testes unitários é garantir

    o funcionamento isolado das unidades do software. O foco dos testes de integração é testar o comportamento geral do software, testando as unidades trabalhando em conjunto.
  30. Teste de Integração Para um website: Um script que usa

    um browser (geralmente sem interface gráfica) para simular fluxos possíveis de um usuário no browser, clicando em botões e preenchendo formulários.
  31. Teste de Integração Para uma API web: Casos de teste

    que fazem requisições nos endpoints e validam a saída.
  32. Teste de Integração Para um chatbot: Um script que simula

    um usuário conversando com o chatbot e valida as respostas do robô.
  33. Teste de Integração Para um módulo Puppet: Aplica um manifesto

    que usa o módulo numa máquina virtual e valida se a máquina ficou configurada do jeito esperado.
  34. Teste de Integração Para uma aplicação de microserviços rodando em

    containers: Um script para subir os containers e interagir com o sistema, validando a saída e se possível validando se os microserviços individualmente interagiram uns com os outros de modo correto.
  35. Teste de Integração Vantagens: Chega próximo do mundo real É

    fácil criá‑los a partir de casos de uso Cobre grande área do código de modo relevante Desvantagens: São caros para rodar (recursos e tempo) Podem ser difíceis de implementar se o ecossistema não tiver um bom ferramental
  36. Performance Além de testar se um software funciona corretamente pode‑se

    também usar asserções para validar comportamentos que influenciam na performance de um código.
  37. Cobertura Uma métrica comum em testes automatizados de caixa branca

    é a contagem de linhas executadas durante a execução dos testes e o cálculo da porcentagem que essa contagem representa.
  38. Cobertura Vantagens Ajuda a descobrir código morto Traz confiança diante

    da necessidade de uma grande refatorada Traz confiança quanto à qualidade geral do software Desvantagens A vontade de atingir cobertura de 100% pode ser cara Não garante que o software tenha menos bugs
  39. Mutantes Ferramentas para testes mutantes geram mutações do seu código

    fonte e executam os casos de teste contra cada um dos mutantes gerados.
  40. Mutantes Uma mutação de um código é uma cópia do

    código com alguma pequena alteração. Original: if user.role == 'admin' user.permissions << 'delete' end Mutante: if user.role != 'admin' user.permissions << 'delete' end
  41. Mutantes A ferramenta espera que o caso de teste passe

    somente com o código original e falhe com todos os mutantes testados. O racional é que se o caso de teste passar com um determinado mutante, o código está incorreto ou o teste não valida corretamente o comportamento do código. http://cosmic‑ray.readthedocs.io/en/latest/theory.html https://github.com/mbj/mutant
  42. Teste de Propriedade Casos de teste geralmente são escritos manualmente.

    Escreve‑se alguns exemplos de entradas possíveis e depois a conferência da saída. A ideia dos testes de propriedade é descrever o que é possível receber de entrada e deixar o computador gerar entradas aleatórias.
  43. Teste de Propriedade from hypothesis import given from hypothesis.strategies import

    text from unittest import TestCase from validators import valid_mail class TestMailValidator(TestCase): @given(text()) def test_reject_spam_mail(self, random_txt): try: valid_mail(random_txt) except Exception as ex: self.fail("valid_mail crashed for: " + random_txt)
  44. Teste de Propriedade Quando (se) o teste falhar crie um

    teste de regressão com a entrada que causou a falha. https://hypothesis.works/