Python e testes com Mock
Esse post foi uma das tarefas de hoje do trabalho. Imaginei que seria mais fácil, mas pelo jeito preciso de mais prática.
Todo o código novo implementado na minha equipe necessariamente deve sair com testes unitários. O que complica os testes são fatores que não fazem parte das coisas que queremos testar: banco de dados, dependências do sistema operacional como usuários, etc. Esse problema já foi resolvido há algum tempo, porém não tenho certeza como que estavam fazendo em python. Para testes unitários não tem dúvidas, é unittest mesmo que já vem com o Python. Mas o que usar para fazer mocks e stubs? (artigo de obrigatória leitura para qualquer desenvolvedor: Mocks Aren't Stubs).
Bom, eu adotei a mox, por ser bem simples: pymox.
Eis o código a ser testado:
import psycopg2
def func1():
conn = psycopg2.connect('dbname=test')
cursor = conn.cursor()
cursor.execute('select count(*) from tabela1')
row = cursor.fetchone()
ret = row[0]
conn.close()
return ret
def func2():
conn = psycopg2.connect('dbname=test')
cursor = conn.cursor()
cursor.execute('select count(*) from tabela2')
row = cursor.fetchone()
return row[0]
O grande problema é o fato de chamarmos direto o connect do módulo psycopg2. Realmente foi um código simples e direto. E como testar? Eis a resposta:
import unittest
import mox
import psycopg2
import psycopg2.extensions
import code_to_test
class MyTest(unittest.TestCase):
def setUp(self):
self.mox = mox.Mox()
def tearDown(self):
self.mox.UnsetStubs()
def test_func1(self):
# Criando os mocks necessarios (substituirao os objetos reais) conn_mock = self.mox.CreateMock(psycopg2.extensions.connection) cursor_mock = self.mox.CreateMock(psycopg2.extensions.cursor) # Substituindo o metodo connect, ponto de entrada do modulo self.mox.StubOutWithMock(psycopg2, 'connect')
# Programando a sequencia de chamadas psycopg2.connect('dbname=test').AndReturn(conn_mock)
conn_mock.cursor().AndReturn(cursor_mock)
cursor_mock.execute('select count(*) from tabela1')
cursor_mock.fetchone().AndReturn([1])
# Objetos programados, agora o teste self.mox.ReplayAll() ret = code_to_test.func1() # Chamei todos os metodos que programei?? self.mox.VerifyAll() # Pronto! Agora a verificacao do estado: self.assertEquals(ret, 1)
O segredo está em criar um stub do método connect. A partir desse ponto, qualquer código chamado subsequente chama apenas mocks.
Olhando novamente, o código poderia ser bem mais simples de testar, mas não é bom acabar com os assuntos em um post só, falarei como outro dia.
Posted at 09:15PM Jul 13, 2009 by ze in Python | Comments[0]


