Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 25: Testes automatizados


A atividade de testar software é uma tarefa repetitiva, demorada e tediosa. Por isso, surgiram várias ferramentas para automatizar testes. Existem dois módulos para testes automatizados que acompanham o Python: doctest e unittest.

O módulo doctest usa as Doc Strings que estão presentes no código para definir os testes do código. A função testmod() do doctest procura por um trecho de texto seja semelhante a uma sessão interativa de Python, executa a mesma sequência de comandos, analisa a saída e faz um relatório dos testes que falharam, com os erros encontrados.

In [6]:
"""
Implementa Fibonacci.
"""

def fib(n):
    """Fibonacci:
    Se n <= 1, fib(n) = 1
    Se n > 1, fib(n) = fib(n - 1) + fib(n - 2)
    
    Exemplos de uso:
    
    >>> fib(0)
    1
    >>> fib(1)
    1
    >>> fib(10)
    100
    >>> [ fib(x) for x in xrange(10) ]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    >>> fib('')
    Traceback (most recent call last):
      File "<input>", line 1, in ?
      File "<input>", line 19, in fib
    TypeError
    >>> 
    """
    if not type(n) is int:
        raise TypeError

    if n > 1:
        return fib(n - 1) + fib(n - 2)
    else:
        return 1

def _doctest():
    """
    Evoca o doctest.
    """

    import doctest
    doctest.run_docstring_examples(fib, globals())

if __name__ == "__main__":

    _doctest()
**********************************************************************
File "__main__", line 16, in NoName
Failed example:
    fib(10)
Expected:
    100
Got:
    89

Se todos os testes forem bem sucedidos, não haverá relatório dos testes.

A saída do código acima é um exemplo de relatório de erros dos testes (a Doc String foi alterada de propósito para gerar um erro).

Usando o módulo unittest, os testes são criados através de uma subclasse da classe unittest.TestCase. Os testes são definidos como métodos da subclasse. Os métodos precisam ter seus nomes iniciando com test para que sejam identificados como rotinas de teste.

Os métodos de teste devem evocar ao terminar um dos métodos:

  • assert_: verifica se uma condição é atingida.
  • assertEqual: verifica se o resultado é igual ao parâmetro passado.
  • AssertRaises: verifica se a exceção é a esperada.

Se houver um método chamado setUp, este será executado antes de cada teste, assim é possível reinicializar variáveis e garantir que um teste não prejudique o outro. O final dos testes, o unittest gera o relatório com os resultados encontrados.

Exemplo:

In [12]:
"""

Usa unittest para testar fib.py.
"""

import fib
import unittest

class TestSequenceFunctions(unittest.TestCase):

    def setUp(self):
        self.seq = range(10)

    def test0(self):
        self.assertEqual(fib.fib(0), 1)

    def test1(self):
        self.assertEqual(fib.fib(1), 1)

    def test10(self):
        self.assertEqual(fib.fib(10), 89)

    def testseq(self):
        fibs = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
        
        for x, y in zip(fibs, [ fib.fib(x) for x in self.seq ]):
            self.assert_(x is y)

    def testtype(self):
        self.assertRaises(TypeError, fib.fib, '')

if __name__ == '__main__':
    
    suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
    unittest.TextTestRunner(verbosity=2).run(suite)

Exemplo de relatório com erros:

..F..
======================================================================
FAIL: test10 (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "unittest1.py", line 22, in test10
    self.assertEqual(fib.fib(10), 89)
AssertionError: 100 != 89

----------------------------------------------------------------------
Ran 5 tests in 0.000s

FAILED (failures=1)

No relatório, o terceiro teste falhou, pois fib.fib(10) retornou 100 ao invés de 89, como seria o esperado.

O unittest oferece uma solução muito semelhante a bibliotecas de testes implementadas em outras linguagens, enquanto o doctest é mais simples de usar e se integra bem com a documentação (as sessões do doctest podem servir como exemplos de uso).

In [1]:
 
Out[1]: