ensaio sobre testes automatizados
DESCRIPTION
* 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.TRANSCRIPT
Ensaio sobre testes automatizadosTDC2014 – 09/08/2014
Gustavo Fonseca @gustavonsecagustavofonseca
1
Níveis de teste
● Testes de aceitação
● Testes de integração
● Testes de sistema
● ...
● Testes de unidade
2
3
Testes de Unidade
4
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
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
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.html7
Associação
8
Exemplo de Composição 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 def __init__(self): 14 self.motor = Motor() 15 16 def dar_partida(self): 17 self.motor.ligar() 18 19 def esta_ligado(self): 20 return self.motor.ligado 21 22 def andar(self): 23 if self.esta_ligado(): 24 print 'Andando... uhul!' 25 else: 26 raise RuntimeError('O carro precisa estar ligado.')
Composição
9
1 import unittest 2 3 from cod3 import Carro, Motor 4 5 6 class MotorTests(unittest.TestCase): 7 """ 8 Os testes foram suprimidos para 9 economizar espaco 10 """ 11 12 class CarroTests(unittest.TestCase): 13 def setUp(self): 14 self.carro = Carro() 15 16 def test_dar_partida(self): 17 self.carro.dar_partida() 18 self.assertTrue(self.carro.esta_ligado()) 19 20 def test_andar(self): 21 self.carro.dar_partida() 22 self.assertIsNone(self.carro.andar()) 23 24 def test_andar_com_carro_desligado_levanta_excecao(self): 25 self.assertRaises(RuntimeError, self.carro.andar) 26
Uma instância de Motor foi criada
Motor().ligar()
10
Isolamento de dependências
dublê
11
__init__ 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 def __init__(self, motor=Motor): 14 self.motor = motor() 15 16 def dar_partida(self): 17 self.motor.ligar() 18 19 def esta_ligado(self): 20 return self.motor.ligado 21 22 def andar(self): 23 if self.esta_ligado(): 24 print 'Andando... uhul!' 25 else: 26 raise RuntimeError('O carro precisa estar ligado.')
Composição via argumento na inicialização do objeto
12
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
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...
Dublês de teste
dummy - fake - stub - mock
http://martinfowler.com/articles/mocksArentStubs.html 15
Stubclass 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.
Mockimport 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
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
"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
Pontos de integração
20
21
Cliente REST
http://git.io/S9YCrg
Grato! Dúvidas?
Gustavo Fonseca @gustavonsecagustavofonseca 22