You are on page 1of 39

Testando cdigo em Python

Elias Dorneles
@eliasdorneles

Ouvi falar de TDD pela


1a vez em 2009

Mas levei anos


para ver vantagem.

YMMV
Sua experincia pode ser diferente!

Com dez anos de


experincia, a gente
ainda adolescente
=)

Complexidade pode te deixar de


corao partido

Por que legal escrever testes?


Consigo ir mais longe
Reduz a quantidade de adivinhao
Por conseguinte, reduz minha ansiedade :)
PORM: no ajuda se falta feedback para coisas
mais amplas (tipo: estou criando o produto certo?)

TDD is cool.
Porm, mais comum me
encontrar escrevendo
testes para cdigo legado.

i nis qui traveiz...

Bugs tendem a
reaparecer...

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

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

Cheap trick
$ python meu_script.py <in.txt >out.txt
$ diff <(python meu_script.py) out.txt
Prs:
1. de graa!
2. se sentir Unx hacker :D

Contras:
1. cobre apenas
o caminho feliz
2. no aponta o problema exato

Para cobrir mais


casos, basta usar
outros arquivos.

Sute 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

Sute de testes
$ cat run_tests.sh
#!/bin/bash
set -e

Fixtures

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

Sute de testes
$ cat run_tests.sh
#!/bin/bash
set -e

Fixtures

Expectativas

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

Sute de testes
$ cat run_tests.sh
#!/bin/bash
Fixtures
set -e
Caso
diff <(python meu_script.py < in1.txt)
diff <(python meu_script.py < in2.txt)
diff <(python meu_script.py < in3.txt)
$ ./run_tests.sh

Expectativas

out1.txt
out2.txt
out3.txt

Sute de testes
$ cat run_tests.sh
#!/bin/bash
Fixtures
set -e
Caso
diff <(python meu_script.py < in1.txt)
diff <(python meu_script.py < in2.txt)
diff <(python meu_script.py < in3.txt)
$ ./run_tests.sh

Sute
Expectativas

out1.txt
out2.txt
out3.txt

Having a test suite is


sweet!

Estrutura de um teste
Dado:
fixtures, inicializando cdigo a ser testado
Quando:
exercitar cdigo a ser testado
Ento:
verifica se resultado o esperado

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)

Estrutura de um teste

Parte mais chata

Dado:
fixtures, inicializando cdigo a ser testado
Quando:
exercitar cdigo a ser testado
Ento:
verifica se resultado o esperado

Estrutura de um teste
Dado:
fixtures, inicializando cdigo a ser testado
Quando:
exercitar cdigo a ser testado
Ento:
verifica se resultado o esperado
Parte mais importante e mais fcil de errar

Verificar a coisa
errada atrapalha
mais do que ajuda

Exemplo com bug na verificao

def test_device_should_be_connected(self):
# when:
report = get_status_report(self.device)
# then:
self.assertTrue('connected' in report)

Para ter certeza que est


checando a coisa certa,
faa o teste falhar

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!

Dubls de teste
(test doubles)
Para quando voc
quiser poupar o oficial

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

Agora, sim!

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

Tipos de dubls de teste


Dummy

s para preencher argumentos (geralmente vazios, tipo None, [], {}, etc)

Fakes

implementao leve duma dependncia (exemplo: DB em memria)

Stubs

simulam respostas prontas para chamadas feitas pelo cdigo sendo testado

Mocks

especificam contrato com cdigo sendo testado (possuem asseres)

Biblioteca mock:
use para stubs ou
para mocks

(demo rpida da
classe mock.Mock)

Python context managers


rock!

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

Context managers ajudam com captura/cleanup


# USANDO:

with script_args(['--opcao1', '--opcao2']):


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

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)

ltimos pensamentos...
Testar a coisa certa, na camada correta, s vezes difcil
de descobrir. Idia: pensar o que mais provvel mudar.
Bom design recebe bem as mudanas - Jos Ricardo
Isto vale para o cdigo dos testes tambm, mas os tipos
de coisas que causam mudanas so diferentes.
Geralmente, cdigo de teste melhor bem direto
(mnima indireo, sem muita herana e/ou OO)

Algumas referncias:

FIM
Thanks!
Elias Dorneles
@eliasdorneles

http://blog.solidcraft.
eu/2012/09/test-driven-trapspart-1.html
http://martinfowler.
com/articles/mocksArentStubs.
html
https://www.destroyallsoftware.
com/screencasts/catalog
https://www.facebook.
com/notes/kent-beck/when-tdddoesntmatter/797644973601702
Livro do Michael Feathers

You might also like