Testando código em Python

Testando código em Python

Slides de apresentação no encontro da Python Floripa.

Dfd7b9492f5c5e49dca373bfdd7a3b1a?s=128

Elias Dorneles

December 10, 2015
Tweet

Transcript

  1. 7.

    • Consigo ir mais longe • Reduz a quantidade de

    adivinhação • Por conseguinte, reduz minha ansiedade :) PORÉM: não ajuda se falta feedback para coisas mais amplas (tipo: estou criando o produto certo?) Por que é legal escrever testes?
  2. 12.

    Cheap trick $ ./meu_script.py <in.txt >out.txt $ diff <(./meu_script.py <in.txt)

    out.txt Prós: 1. de graça! 2. se sentir Unıx hacker :D Contras: 1. cobre apenas o caminho feliz 2. não aponta o problema exato
  3. 14.

    Suíte de testes $ cat run_tests.sh #!/bin/bash set -e diff

    <(python meu_script.py < in1.txt) out1.txt diff <(python meu_script.py < in2.txt) out2.txt diff <(python meu_script.py < in3.txt) out3.txt $ ./run_tests.sh
  4. 15.

    Suíte de testes $ cat run_tests.sh #!/bin/bash set -e diff

    <(python meu_script.py < in1.txt) out1.txt diff <(python meu_script.py < in2.txt) out2.txt diff <(python meu_script.py < in3.txt) out3.txt $ ./run_tests.sh Fixtures
  5. 16.

    Suíte de testes $ cat run_tests.sh #!/bin/bash set -e diff

    <(python meu_script.py < in1.txt) out1.txt diff <(python meu_script.py < in2.txt) out2.txt diff <(python meu_script.py < in3.txt) out3.txt $ ./run_tests.sh Fixtures Expectativas
  6. 17.

    Suíte de testes $ cat run_tests.sh #!/bin/bash set -e diff

    <(python meu_script.py < in1.txt) out1.txt diff <(python meu_script.py < in2.txt) out2.txt diff <(python meu_script.py < in3.txt) out3.txt $ ./run_tests.sh Fixtures Expectativas Caso
  7. 18.

    Suíte de testes $ cat run_tests.sh #!/bin/bash set -e diff

    <(python meu_script.py < in1.txt) out1.txt diff <(python meu_script.py < in2.txt) out2.txt diff <(python meu_script.py < in3.txt) out3.txt $ ./run_tests.sh Fixtures Expectativas Suíte Caso
  8. 20.

    Estrutura de um teste ➔ Dado: ◆ fixtures, inicializando código

    a ser testado ➔ Quando: ◆ exercitar código a ser testado ➔ Então: ◆ verifica se resultado é o esperado
  9. 21.

    “Show me some code!” import unittest class ExampleCase(unittest.TestCase): def test_something(self):

    # given: fixture = self.build_fixture(...) # when: resultado = codigo_a_testar(fixture) # then: self.assertEqual(esperado, resultado)
  10. 22.

    Estrutura de um teste ➔ Dado: ◆ fixtures, inicializando código

    a ser testado ➔ Quando: ◆ exercitar código a ser testado ➔ Então: ◆ verifica se resultado é o esperado Parte mais chata
  11. 23.

    Estrutura de um teste ➔ Dado: ◆ fixtures, inicializando código

    a ser testado ➔ Quando: ◆ exercitar código a ser testado ➔ Então: ◆ verifica se resultado é o esperado Parte mais importante e mais fácil de errar
  12. 25.

    Exemplo com bug na verificação def test_device_should_be_connected(self): # when: report

    = get_status_report(self.device) # then: self.assertTrue('connected' in report)
  13. 27.

    Fazendo o teste falhar def test_device_should_be_connected(self): # when: report =

    get_status_report(self.offline_device) # then: self.assertTrue('connected' in report) OOOPS! Teste continua passando!
  14. 28.

    Corrigindo... def test_device_should_be_connected(self): # when: report = get_status_report(self.offline_device) # then:

    self.assertRegexpMatches(report, r'\bconnected') Agora o teste falha, como esperado
  15. 31.

    Tipos de dublês de teste ➔ Dummy ◆ só para

    preencher argumentos (geralmente vazios, tipo None, [], {}, etc) ➔ Fakes ◆ implementação leve duma dependência (exemplo: DB em memória) ➔ Stubs ◆ simulam respostas prontas para chamadas feitas pelo código sendo testado ➔ Mocks ◆ especificam contrato com código sendo testado (possuem asserções)
  16. 35.

    Context managers ajudam com captura/cleanup @contextmanager def script_args(argv): old_argv =

    sys.argv try: sys.argv = ['fake_script.py'] + argv yield sys.argv finally: sys.argv = old_argv
  17. 36.

    Context managers ajudam com captura/cleanup # USANDO: with script_args(['--opcao1', '--opcao2']):

    print(sys.argv) # vai mostrar: # ['fake_script.py', '--opcao1', '--opcao2']
  18. 37.

    Mais ferramentas legais • VCR.py https://pypi.python.org/pypi/vcrpy • Tox: https://tox.readthedocs.org •

    py.test http://pytest.org/ • Nose: https://nose.readthedocs.org Wishlist: RSpec para Python (http://rspec.info)
  19. 38.

    Últimos pensamentos... • Testar a coisa certa, na camada correta,

    às vezes é difícil de descobrir. Idéia: pensar o que é mais provável mudar. • “Bom design recebe bem as mudanças” - José Ricardo ◦ Isto vale para o código dos testes também, mas os tipos de coisas que causam mudanças são diferentes. ◦ Geralmente, código de teste é melhor bem direto (mínima indireção, sem muita herança e/ou OO)
  20. 39.

    FIM Thanks! Algumas referências: • http://blog.solidcraft. eu/2012/09/test-driven-traps- part-1.html • http://martinfowler.

    com/articles/mocksArentStubs. html • https://www.destroyallsoftware. com/screencasts/catalog • https://www.facebook. com/notes/kent-beck/when-tdd- doesnt- matter/797644973601702 • Livro do Michael Feathers Elias Dorneles @eliasdorneles