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

Testando código em Python

Testando código em Python

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

Elias Dorneles

December 10, 2015
Tweet

More Decks by Elias Dorneles

Other Decks in Programming

Transcript

  1. Testando código em Python
    Elias Dorneles
    @eliasdorneles

    View Slide

  2. Ouvi falar de TDD pela
    1a vez em 2009

    View Slide

  3. Mas levei anos
    para ver vantagem.

    View Slide

  4. YMMV
    Sua experiência pode ser diferente!

    View Slide

  5. Com dez anos de
    experiência, a gente
    ainda é adolescente
    =)

    View Slide

  6. Complexidade pode te deixar de
    coração partido

    View Slide

  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?

    View Slide

  8. TDD is cool.
    Porém, é mais comum me
    encontrar escrevendo
    testes para código legado.

    View Slide

  9. Bugs tendem a
    reaparecer...
    Ói nóis qui traveiz...

    View Slide

  10. Regra de ouro:
    quando encontrar bug,
    adicione um caso para
    pegá-lo.

    View Slide

  11. Cheap trick
    $ ./meu_script.py out.txt
    $ diff <(./meu_script.py

    View Slide

  12. Cheap trick
    $ ./meu_script.py out.txt
    $ diff <(./meu_script.py 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

    View Slide

  13. Para cobrir mais
    casos, basta usar
    outros arquivos.

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  19. Having a test suite is
    sweet!

    View Slide

  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

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  24. Verificar a coisa
    errada atrapalha
    mais do que ajuda

    View Slide

  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)

    View Slide

  26. Para ter certeza que está
    checando a coisa certa,
    faça o teste falhar

    View Slide

  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!

    View Slide

  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

    View Slide

  29. Agora, sim!
    def test_device_should_be_connected(self):
    # when:
    report = get_status_report(self.device)
    # then:
    self.assertRegexpMatches(report, r'\bconnected')

    View Slide

  30. Dublês de teste
    (test doubles)
    Para quando você
    quiser poupar o oficial

    View Slide

  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)

    View Slide

  32. Biblioteca mock:
    use para stubs ou
    para mocks

    View Slide

  33. (demo rápida da
    classe mock.Mock)

    View Slide

  34. Python context managers
    rock!

    View Slide

  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

    View Slide

  36. Context managers ajudam com captura/cleanup
    # USANDO:
    with script_args(['--opcao1', '--opcao2']):
    print(sys.argv)
    # vai mostrar:
    # ['fake_script.py', '--opcao1', '--opcao2']

    View Slide

  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)

    View Slide

  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)

    View Slide

  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

    View Slide