Skip to content

Teste unitário automático em Python

Suponha que um cliente (um cliente muito estranho) te pediu para implementar uma função que calculasse o número de fibonacci.

Então você criou o seguinte código (em Python):

def fibo(n):
   if n < 2:
      return n
   else:
      return fibo(n-1) + fibo(n-2)

Devido ao alto número de chamadas recursivas você nunca criaria esse código. Ele só está aí a título de ilustração. =)

O código está pronto, será que acabou o serviço? Como saber se os requisitos foram atendidos? Os testes unitários podem te ajudar. Basicamente você o comportamento da função para certas entradas e se elas se comportarem bem para essas entradas você admite que ela está correta. Por exemplo, você sabe que fibonacci de 1 é 1 e que o fibonacci de 10 é 55. O que você faria normalmente seria abrir um console Python ou criaria um programa de teste para testar esses valores.

O problema é que depois de um tempo alguém pode alterar a função que você criou. Ou pior, alguém pode alterar um código numa outra parte do programa que o seu código usava para fazer o serviço. Um bom discípulo da engenharia de software iria fazer novos testes unitários toda vez que alguém mexer no código, mas um discípulo ninja mesmo, vai automatizar essa tarefa.

Se você estiver trabalhando com Python há pelo menos duas formas fáceis de você automatizar essa tarefa usando os módulos doctest ou unittest. Esse módulos já vem com o Python por padrão.

Doctest

Com o doctest você só cria testes unitários simplesmente adicionando algumas linhas de comentários ao seu código e depois fazendo uma chamada ao doctest:

def fibo(n):
   """
   >>> fibo(0)
   0
   >>> fibo(1)
   1
   >>> fibo(10)
   55
   """
   if n < 2:
      return n
   else:
      return fibo(n-1) + fibo(n-2)

import doctest
doctest.testmod()

As últimas duas linhas do programa importam o módulo doctest e pedem para testar aquele programa. Se você salvar o programa como fibo.py e executa-lo, nada aparecerá. Isso é bom, quer dizer que você fez tudo certinho. Vamos supor que eu alterei a função fibo de forma que fibo(10)=20, ou seja, um erro. Então você obtem esse comportamento:

$ python fibo.py
****************************
File "fibo.py", line 7, in __main__.fibo
Failed example:
fibo(10)
Expected:
55
Got:
20
****************************
1 items had failures:
1 of 3 in __main__.fibo
***Test Failed*** 1 failures.

Excelente não é? Se você quiser um relatório mais detalhado use a opção verbose do doctest usando a linha de comando python fibo.py -v.

Unittest

Como o nome já diz, o módulo unittest foi feito para fazer testes unitários. Com ele você pode fazer coisas mais avançadas além de testar coisas como função(parâmetro) = resultado_esperado. na verdade o módulo unittest é um framework para testes unitários.

No exemplo, vamos importar a função fibo do arquivo fibo.py e depois criar uma clase do tipo TestCase.

import unittest
from fibo import fibo

class testa_fibonacci(unittest.TestCase):
   def teste_um(self):
      self.assertEqual(fibo(0),0)

   def teste_dois(self):
      self.assertEqual(fibo(1),1)

   def teste_tres(self):
      self.assertEqual(fibo(7),13)

   def teste_quatro(self):
      self.assertEqual(fibo(10),55)

unittest.main()

A execução dele seria assim:

$ python teste.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

Isso foi só uma brevíssima introdução sobre essas ferramentas. Elas conseguem fazer coisas que você nem imagina. Se você se interessa no assunto eu recomendo fortemente a leitura da documentação do módulo unittest.

Para saber mais: Quality Control in Python, Documentação oficial do unittest e Documentação oficial do módulo doctest.

Published inUncategorized

4 Comments

  1. Stephen Stephen

    “Devido ao alto número de chamadas recursivas você nunca criaria esse código. Ele só está aí a título de ilustração. =)”

    Só uma observação: Caso se esteja usando o Stackless Python, a recursão não é problema, já que ele não usa a pilha para chamadas de função.

    Em algumas linguagens, que otimizam “tail calls” (como Scheme), a recursão é até incentivada.

    Post interessante sobre o assunto(para o próprio exemplo do Fibonacci, no caso do Python): http://www.tratt.net/laurie/tech_articles/articles/tail_call_optimization

    O pessoal do TextMate fez um screencast interessante, mostrando um módulo parecido com o DocTest para Ruby(no caso, para o Triângulo de Pascal).

    Legal é a abordagem que ele usa, que é pegar o próprio exemplo de um triângulo de Pascal e jogar direto dentro do código. O foco é o próprio TextMate, mas a idéia é interessante. Dá pra fazer a mesma coisa em Python.

    http://macromates.com/screencast/ruby_quiz_screencast.mov

  2. Rapaz, um desafio pra vc:
    imprimir 2 matrizes diferentes, com x elementos cada uma, que não se repetem dentro das matrizes nem na mesma posição do seu correspondente da outra matriz. Exemplo:

    matriz1 [0,3,4,1,5,2]
    matriz2 [3,2,1,5,4,0]

    Ou seja, matriz1[0] != matriz2[0]; matriz1[1] != matriz2[1], e assim sucessivamente.

  3. Boa silveira, não trabalho com python, mas em java a técnica de TDD é muito importante, pena ainda não termos essa cultura aqui no brasil principalmente no ceará, mas com o tempo vamos adicionando isso ao mercado.

    Abraços

  4. hugobenicio hugobenicio

    Muito bom artigo! Meus códigos em python agora vão estar mais seguros e fáceis de se testar! (principalmente contra estagiários malucos fuçadores de códigos! ;D)

    Continue com artigos sobre python! ;D
    abraço!

Leave a Reply

Your email address will not be published. Required fields are marked *