Buscar

Testes unitários com pytest

Prévia do material em texto

Testes 
unitários para
profissionais
de dados com
pytest
O guia definitivo - parte I
Suponha que você precisou implementar uma função para um
script que você está desenvolvendo.
Como você normalmente testa se a função foi implementada
corretamente?
O jeito mais “simples” seria chamar a função algumas vezes,
passando diferentes argumentos.
Se der certo, você assume que a função foi implementada
corretamente e vida que segue.
Embora esse método seja o mais simples e fácil de testar sua
função, ele é muito ineficiente.
Isso fica bem evidente quando pensamos no ciclo de vida de
uma função em um projeto de dados, que normalmente tem
essa cara:
1 Vinícius Galvão
Implementação Testes
Sucesso
Falha
Implementação
aceita
Correção de bugs
Bugs 
encontrados
Solicitação de 
feature ou 
refatoração
Nós implementamos a função, testamos, e caso passe no
teste, nós implementamos.
Caso a função falhe, nós encontramos os erros que
encontramos e testamos novamente.
Tempos depois, precisamos implementar uma nova feature ou
refatorar a função, para então testarmos novamente.
2 Vinícius Galvão
Nesse momento, é bastante comum descobrir bugs não
previstos antes. Com isso, corrigimos o bug e testamos
novamente.
Consegue perceber a quantidade de vezes que a função
precisou ser testada em seu ciclo de vida?
Se o projeto durar alguns anos, a função pode chegar a ser
testada centenas de vezes.
Agora vamos estimar que cada vez que a função foi testada
foram gastos, em média, 10 minutos. Se calcularmos a
quantidade de horas destinadas a esses testes, nas 100
primeiras vezes, serão gastos um pouco mais de 16 horas (10
min x 100).
É nesse momento que vemos a importância dos testes
unitários. Com eles, é possível automatizar o processo
repetitivo de testes e, com isso, economizar bastante
tempo.
3 Vinícius Galvão
Imagine que você implementou a função cast_to_int em
preprocessing_helpers.py, que recebe um número em formato
de string e converte para um inteiro.
Se quisermos testar essa função, podemos fazer da seguinte
maneira:
Escrevendo seu primeiro teste unitário
 
Perceba que a função ainda retornou uma string, ou seja, ela
não converteu a string para um inteiro.
Outro teste que podemos fazer é passar um inteiro para a
função e analisar o que ela retornará.
4 Vinícius Galvão
pytest
unittest
doctest
Observe que a função retornou um erro, pois passamos um
argumento que não é string para a função.
Poderíamos fazer vários outros testes manuais e analisar o
que a função irá retornar, porém, como já discutimos, é muito
mais produtivo criarmos testes unitários para essa função.
Existem várias bibliotecas que nos ajudam a desenvolver
testes unitários em Python.
Para esse tutorial, escolhi o pytest, por ser o mais popular e,
na minha opinião, o mais fácil de utilizar.
5 Vinícius Galvão
Crie um arquivo chamado test_cast_to_int.py.
Existe uma convenção de que os arquivos começados com
test_ indicam que há testes unitários neles.
Defina o argumento de teste que será passado para a função,
o resultado esperado e o resultado obtido.
6 Vinícius Galvão
1. Crie um arquivo de teste
2. Importe a função a ser testada
3. Crie a função de teste
Caso você não conheça como funciona o assert, por
enquanto, basta entender que ele analisa uma expressão que
retorna um boleano, caso a expressão seja verdadeira, ele não
retorna nada, caso a expressão seja falsa, ele retorna um
AssertionError. Por exemplo:
No nosso caso, queremos comparar se a função cast_to_int
retornou o resultado que esperávamos. Então ficamos com:
Ou seja, esperamos que cast_to_int(“2.021”) seja igual à 2021.
7 Vinícius Galvão
4. Assertion
Escreva isso no terminal:
O seguinte relatório é gerado:
Vamos entender o resultado por partes.
8 Vinícius Galvão
5. Executar o teste unitário
O caractere F informa que o teste falhou e, com isso, devemos
corrigir a função ou o teste unitário.
Seguindo adiante, temos informações dos testes que
falharam.
Primeiro, o relatório trás algumas informações gerais,
indicando que os testes foram iniciados.
Em seguida, temos o resultado do teste.
9 Vinícius Galvão
Perceba que a linha que traz a exceção é marcada com um >.
Além disso, a exceção é uma AssertionError.
Por fim, temos um resumo dos resultados dos testes.
Vale ressaltar que o teste foi executado em 0.16 segundos,
muito mais rápido se compararmos com os testes manuais.
10 Vinícius Galvão
Agora vamos analisar nossa função cast_to_int() e identificar
o porquê que ela não está retornando o resultado esperado.
Observe que para que a função retorne um inteiro,
precisamos colocar o resultado dentro de um int(), ficando
dessa forma:
Agora, se executarmos novamente nosso teste unitário,
obtemos o seguinte relatório:
11 Vinícius Galvão
Observe que agora no lugar de
Temos
O ponto verde indica que nosso teste passou!
12 Vinícius Galvão
Bônus I. Adicionando mensagens nos
testes
Nós podemos ainda adicionar uma mensagem para os casos
em que o resultado da expressão booleana for falso.
Por exemplo:
Com isso, podemos adicionar uma mensagem para o nosso
teste unitário da seguinte maneira:
Caso a expressão actual == expected for Falsa, a mensagem
será apresentada como uma AssertionError. Podendo ser
bastante útil para identificar possíveis erros.
13 Vinícius Galvão
Vimos que ela retorna um AttributeError, indicando que um
float não possui o atributo replace.
Veremos agora como podemos criar testes unitários para esse
tipo de situação.
Se escrevermos a função test_on_float():
No início do tutorial, nós testamos nossa função, cast_to_int(),
passando um float como argumento.
Dessa maneira, caso a função retorne um AttributeError, o
teste passa.
Bônus II. Testes com exceções
14 Vinícius Galvão
Nesse caso, expection_info armazena o AttributeError e
exception_info.match(expected_msg) checa se expected_msg
está presente no erro retornado.
Nosso arquivo test_cast_to_int.py agora possui essa cara:
Podemos ainda testar se a mensagem apresentada no erro
está de acordo com o que esperamos.
Bônus II. Testes com exceções
15 Vinícius Galvão
Podemos executar novamente nosso teste unitário para
analisarmos os resultados.
Perceba que os dois pontos verdes indicam que os dois testes
passaram.
Bônus II. Testes com exceções
16 Vinícius Galvão
Nós podemos ainda utilizar mais de um assert em um teste
unitário.
Por exemplo, se quisermos que o nosso teste verifique
também se o resultado da função cast_to_int() retornou um
inteiro, podemos fazer da seguinte maneira:
Bônus III. Múltiplos asserts em um
único teste unitário
17 Vinícius Galvão
Uma boa prática é encapsular os testes unitários de uma
função em uma classe.
Aplicando aos nossos teste unitários, temos que
Bônus IV. Testes unitários com classes
18 Vinícius Galvão
Vinícius Galvão
salve para quando precisar
O material te ajudou?

Continue navegando