5 libs essenciais para testes unitários Python

Paula Grangeiro
Paula Grangeiro
Published in
4 min readJun 2, 2017

--

Quem me conhece sabe que sou uma desenvolvedora apaixonada por escrever testes (unitários especificamente). Acredito que testes unitários são uma excelente ferramenta para medir a qualidade do código, auxiliam na documentação do projeto e que por esses e outros tantos motivos são indispensáveis do ciclo de desenvolvimento de software.

Alguns desenvolvedores mais ceticistas já me questionaram sobre o real ganho dessa abordagem: “Por que desperdiçar tempo escrevendo testes quando eu deveria estar escrevendo código que solucionam problemas reais?” É bem verdade que se você não possui o hábito de escrever testes, codar um único teste unitário pode levar tanto tempo quanto escrever código de qualidade para produção. Os desafios na mudança de mindset — escrever código de teste exige uma abordagem diferente de escrever código de produção — aliada a dificuldade técnica inicial (comportamentos inesperados ou simplesmente não saber como testar o quê) podem de um primeiro momento afastar aqueles que começaram a se aventurar recentemente neste caminho.

Mas se você chegou até aqui e ainda te resta um pouco de energia, saiba que (como em tudo na programação) a prática leva a perfeição. Então para tornar a sua caminhada menos árdua, segue a lista de libs que considero uma mão na roda na hora de escrever testes unitários em Python.

Mock

Se esta é uma palavra nova no seu vocabulário de programação, segue uma breve descrição do termo segundo a Wikipédia:

Objetos Mock, objetos simulado ou simplesmente Mock (do inglês Mock object) (…) são objetos que simulam o comportamento de objetos reais de forma controlada.

Ou seja, objetos mock substituem objetos reais, em tempo de execução, de maneira que nós programadores possamos verificar aspectos comportamentais que de outra forma não seriam acessíveis — como por exemplo se determinada função foi chamada com os argumentos esperados.

foo = Mock(Foo) # cria um objeto Mock a partir da classe Foo
foo.bar('Olar') # chama o método da classe, bar
foo.bar.assert_called_once_with('Olar')
# verifica se o método bar foi chamado, uma única vez (once), com o argumento 'Olar' como esperado

Mocks são ferramentas essenciais para garantir o isolamento do código de teste, pois através deles é possível mockar chamadas externas à funcionalidade que está sendo testada — prometo falar mais sobre isso futuramente.

Para mais detalhes sobre a lib, leia a documentação para Python 2 e Python 3.

vcrpy

Em testes que precisam lidar com requests externas, dependendo das características da url requisitada, pode ser trabalhoso criar um objeto mock que represente esse request com fidelidade. Para estes casos, dê uma chance ao vcrpy.

with vcr.use_cassette('tests/fixtures/vcr/fake_request.json'):
# Tudo que estiver dentro dessa clausula with, será feito
# utilizando o arquivo "cassette" especificado
# Se o arquivo "cassette" nao existir, será criado um
response = self.client.get('http://google.com')
# Se o arquivo "cassette" estiver vazio (for recém criado)
# Faz uma requisicao real ao dominio e armazena os parametros
# e atributos desta requisicao no arquivo "casette"
assert 200 == response.status_code

O vcrpy é uma lib para facilitar o mock de objetos requests. Ela faz isso armazenando todos os dados de um request real em um arquivo, o qual ele chama de cassette. Esses dados serão utilizados na criação do mock em requests futuros para a mesma url. Tudo isso automaticamente.

freezegun

Em certas situações, seria bom se pudéssemos congelar o tempo… Principalmente quando escrevemos testes que lidam com coisas do tipo datetime.now(). Pois com o freezegun isto é possível.

@freeze_time("1955-11-12 08:01")
class MyTests(unittest.TestCase):
def test_the_class(self):
assert datetime.datetime.now() == datetime.datetime(1955, 11, 12, 8, 1)

O decorator freeze_time pode ser utilizado para decorar classes, métodos ou ainda como um método em conjunto com a cláusula with — caso você queira utilizar uma data específica em alguma asserção.

with freeze_time("2012-01-14"):
assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)

model_mommy

A lib model_mommy é uma mão na roda na hora de elaborar fixtures para projetos Django. Com ela, é possível criar rapidamente instâncias de classes para os seus testes.

dog = mommy.prepare(Dog, name='Pluto') 
# cria uma instância do modelo Dog de nome Pluto
assert 'Pluto' == dog.name

Essas instâncias podem ser apenas criadas em memória(utilizando o método prepare) ou refletidas no banco de dados (utilizando o método make). Caso a classe utilizada possua atributos obrigatórios e os mesmos não sejam informados como parâmetro, o model_mommy utilizará dados randômicos — de acordo com o tipo do atributo.

>>> instance = mommy.make(Insurer)
>>> # classe que possui um atributo name obrigatorio
>>> print(instance.name)
ZUhdexZejmnemEfYSqhJxvoFlTfKShkzwBmVfXFqzfKpVoAUhmSLcEPtyhcwOqwpHAbRBICPDKNPQQkjGsbPeYAWmZsrtIfVXXUWbnjLRJcYSFjHyedCwfIFACqAKtJBYckvYsCbWZFKPPdWBEdPFahNgxoKgDTFyikIbgXxIYSklEvTUTLjWdssaeVNAfsjcsxaxBJRbbvHpnKoYukdTmmGxfpsLNuedbcFSeAEREgFBVkCoNAMNBIfodbcPnJ

faker

Por vezes, precisamos de fixtures com dados um pouco mais “inteligentes”. Para estes casos, existe a lib faker.

>>> fake = Faker()>>> fake.name()
Lucy Cechtelar
>>> fake.address()
426 Jordy Lodge Cartwrightshire, SC 88120-6700

A lib faker cria dados randômicos amigáveis e se utilizada em conjunto com a model_mommy podemos criar fixtures mais interessantes.

>>> fake = Faker()
>>> instance = mommy.make(
... Person,
... name=fake.name(),
... age=fake.pyint(),
)
>>> print(instance.name, instance.age)
John Doe, 26

E aí? Pronto para perder menos tempo codando testes? Tem alguma indicação legal que não foi citada aqui? Diz aí nos comentários!

--

--

Programadora por profissão, desenhista nas horas vagas e colecionadora de gatos.