Baixe o app para aproveitar ainda mais
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?
Compartilhar