* Slides da apresentação realizada no TDC São Paulo, 2014.
* A apresentação é um "fork" da realizada no FISL14, em 2013, entitulada O que você deve saber sobre Unit Tests.
--
Testes automatizados é um tema que frequentemente atrai a atenção dos novatos e prende a atenção dos programadores mais experientes. Boa parte das dúvidas concentram-se em o que testar, como testar e as melhores práticas para testar determinado código ou funcionalidade e, um empurrãozinho pode fazer toda a diferença para quem está começando. O objetivo dessa palestra é de expor, de maneira prática, minhas experiências de sucesso e insucesso no design de códigos testáveis assim como dos seus testes.
5. O que é Teste de Unidade?
1 #coding: utf-8
2
3 def saudar(nome):
4 return u'Olá, %s.' % nome
5
6
7 # garantir que o nome do fulano passado como
8 # argumento seja utilizado na saudação
9 assert saudar('Gustavo') == u'Olá, Gustavo.'
10
11 # garantir que seja retornado um objeto unicode
12 assert isinstance(saudar('foo'), unicode)
Unidade objeto dos testes
Casos de teste
5
6. Características
● Garantir o isolamento dos testes
● Facilitar a configuração do ambiente
● Fomentar a organização dos testes
● Executar os testes
● Produzir relatórios úteis
6
7. 1 #coding: utf-8
2 import unittest
3
4
5 def saudar(nome):
6 return u'Olá, %s.' % nome
7
8
9 class SaudarTests(unittest.TestCase):
10
11 def test_nome_do_fulano_seja_utilizado_na_saudacao(self):
12 self.assertEqual(saudar('Gustavo'), u'Olá, Gustavo.')
13
14 def test_objeto_retornado_tipo_unicode(self):
15 self.assertIsInstance(saudar('foo'), unicode)
Resumo da execução
dos testes
Casos de teste são
agrupados em
subclasses de
unittest.TestCase
O ambiente pode ser
preparado por meio
dos métodos setUp e
tearDown
unittest (stdlib)
http://docs.python.org/2.7/library/unittest.html
7
13. Atributo de classe
1 class Motor(object):
2 def __init__(self):
3 self.ligado = False
4
5 def ligar(self):
6 self.ligado = True
7
8 def desligar(self):
9 self.ligado = False
10
11
12 class Carro(object):
13 classe_motor = Motor
14
15 def __init__(self):
16 self.motor = self.classe_motor()
17
18 def dar_partida(self):
19 self.motor.ligar()
20
21 def esta_ligado(self):
22 return self.motor.ligado
23
24 def andar(self):
25 if self.esta_ligado():
26 print 'Andando... uhul!'
27 else:
28 raise RuntimeError('O carro precisa estar ligado.') 13
Composição via atributo
de classe
14. Atributo de classe (cont.)
14
from unittest import mock # python3
!
with mock.patch.object(Carro, 'classe_motor', autospec=True) as motor_teste:
motor_teste.return_value = MotorDuble
!
carro = Carro()
# testes aqui...
15. Dublês de teste
dummy - fake - stub - mock
http://martinfowler.com/articles/mocksArentStubs.html 15
16. Stub
class CarroTests(unittest.TestCase):
def setUp(self):
!
class MotorStub(object):
def __init__(self):
self.ligado = False
def ligar(self): return None
def desligar(self): return None
!
self.carro = Carro(motor=MotorStub)
!
def test_dar_partida(self):
self.carro.motor.ligado = True
!
self.carro.dar_partida()
self.assertTrue(self.carro.esta_ligado())
!
def test_andar(self):
self.carro.motor.ligado = True
!
self.carro.dar_partida()
self.assertIsNone(self.carro.andar())
!
def test_andar_com_carro_desligado_levanta_excecao(self):
self.assertRaises(RuntimeError, self.carro.andar)
!
Implementa a
interface do objeto,
com retornos
previsíveis.
O dublê entra em ação! rá!
16
Configuração de estado
pré-teste.
17. Mock
import mocker # python 2 apenas =/
!
from cod3 import Carro
!
!
class CarroTests(mocker.MockerTestCase):
!
def test_dar_partida(self):
# Inicio do treinamento
MotorMock = self.mocker.mock()
!
MotorMock()
self.mocker.result(MotorMock)
!
MotorMock.ligar()
self.mocker.result(None)
!
MotorMock.ligado
self.mocker.result(True)
!
self.mocker.replay()
# Fim do treinamento
!
carro = Carro(motor=MotorMock)
carro.dar_partida()
self.assertTrue(carro.esta_ligado())
Ver também: http://docs.python.org/3.3/library/unittest.mock.html
labix.org/mocker
17
18. import mocker
!
from cod3 import Carro
!
!
class CarroTests(mocker.MockerTestCase):
!
def test_dar_partida(self):
# Inicio do treinamento
MotorMock = self.mocker.mock()
!
MotorMock()
self.mocker.result(MotorMock)
!
MotorMock.ligar()
self.mocker.result(None)
!
#MotorMock.ligado
#self.mocker.result(True)
!
self.mocker.replay()
# Fim do treinamento
!
carro = Carro(motor=MotorMock)
carro.dar_partida()
self.assertTrue(carro.esta_ligado())
Removido o treino do
atributo .ligado
18
19. 19
"Mocke" com moderação
• Testes menos manuteníveis
• Mudanças de interface (API)
• Inconsistências entre "treinamentos"
• Prefira "mockar" os pontos de integração
• Teste os mocks! o.O