Tuesday, December 24, 2019

Evitando chamadas custosas em testes automáticos

Ao escrever testes automáticos, é comum utilizarmos Test Doubles para substituir dependências diretas do código sob teste. Além de ter impacto direto na forma de pensar e construir tanto os casos de teste quanto o próprio código sob teste, essa prática também permite eliminar chamadas custosas e/ou não confiáveis durante a execução dos testes, garantindo assim mais eficiência e confiabilidade para a suíte de testes como um todo.

Por exemplo, suponha que precisemos testar uma função ou objeto que efetue uma requisição HTTP para uma API externa à nossa aplicação. Podemos criar um mock para o trecho de código que faz a requisição, de forma a simular o retorno esperado e, assim, não ter que fazer a chamada real de rede. Ganhamos em eficiência, pois, sem a requisição, não é necessário lidar com a latência de rede durante a execução dos testes. Ganhamos também em confiabilidade, pois, como chamadas HTTP podem falhar, qualquer requisição externa tem o potencial de gerar um falso positivo na suíte de testes; removendo a chamada, removemos essa possibilidade.

Pensando nisso, é de vital importância que numa base de código grande — com uma suíte de testes, em geral, igualmente grande — consigamos automaticamente garantir que chamadas custosas sejam apropriadamente mockadas. No caso de requisições de rede, podemos simplesmente bloquear ou restringir chamadas de socket durante a execução dos testes. É o que o pytest-socket, por exemplo, faz. Para quem usa o pytest, basta instalar o plugin e executar pytest --disable-socket. Qualquer teste em que uma chamada de socket for feita (mesmo por pacotes terceiros importados) irá falhar com SocketBlockedError.

Para códigos com custo de computação alto (ex: uma função com um pesado cálculo recursivo) que façam parte da aplicação, uma forma de evitar seu uso sem mock em testes é torna esse requisito uma parte do próprio código, isto é, permitir que a pessoa que o desenvolveu adicione alguma marcação que bloqueie a chamada em ambiente de teste. Não conhecendo uma solução já existente para isso em Python, me aventurei em criar a minha própria e daí surgiu o decorador enforce_mock_in_tests. Esse decorador pode ser aplicado a classes ou funções. A cada chamada do código decorado, ele avalia se o ambiente é de teste ou não e, se for, uma exceção é lançada, dando uma clara indicação ao desenvolvedor de que aquele código não deve ser chamado diretamente nos casos de teste, mas sim mockado. Além do código do decorador em si, o Gist abaixo contém também um arquivo com casos de teste para ele, permitindo ver como o decorador pode ser usado no código da aplicação.

No comments

Post a Comment