Testar no percurso de Python

Aprende a testar os teus exercícios de Python no Exercism.


Usamos o pytest como executor de testes do site. Precisas de instalar o pytest na tua máquina de desenvolvimento se quiseres descarregar e correr os testes dos exercícios do track de Python localmente. Também deves instalar os seguintes plugins do pytest:

Recomendamos também o programa de análise de código pylint, pois faz parte do nosso feedback automatizado no site e pode ser uma ferramenta de análise estática de código muito útil. Para maior comodidade, o plugin pytest-pylint do pytest permite-te executar o pylint através do pytest na linha de comandos.

A configuração do Pylint pode ser um pouco complicada, por isso este tutorial de pylint.readthedocs.io pode ajudar-te a começar, tal como esta visão geral de Qualidade de código: ferramentas e boas práticas da Real Python.

Instalar o pytest

O pytest pode ser instalado e atualizado com o utilitário pip incorporado no Python.

Para mais dicas, o Brett Cannon tem um bom guia rápido e descomplicado sobre como instalar pacotes para Python, juntamente com uma excelente explicação sobre por que motivo deves usar python -m pip. Para saberes mais sobre os argumentos da linha de comandos do Python, consulta linha de comandos e ambiente na documentação do Python.

Nota: o Python3 e o py podem ou não ser aliases do Python no teu sistema. Ajusta os comandos de instalação abaixo em conformidade. Para instalar o pytest num ambiente virtual, certifica-te de que o ambiente está ativado antes de executar os comandos. Caso contrário, a instalação do pytest será global.

Windows

PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

Linux / MacOS

$ python3 -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

Para verificar se a instalação foi bem-sucedida:

$ python3 -m pytest --version
pytest 8.3.3

Correr os testes

Para correr os testes, vai para a pasta onde o exercício está guardado com cd no teu terminal (substitui <exercise-folder-location> abaixo pelo teu caminho).

$ cd <exercise-folder-location>

Note

O <exercise-folder-location> ou a maioria das coisas entre sinais de menor e maior representam um valor de substituição. Um caminho ou nome de ficheiro normal deve ser escrito sem quaisquer parênteses.

Por exemplo: /Users/janedoe/exercism/python/exercises/concept/chaitanas-colossal-coaster (em sistemas *nix), C:\Users\janedoe\exercism\python\exercises\practice\hello-world\ (no Windows), myFolder ou my_file.py.


O ficheiro que vais querer correr termina habitualmente em _test.py. Este ficheiro contém os testes para a solução do exercício e são os mesmos testes que correm no site quando uma solução é enviada. Em seguida, corre o seguinte comando no teu terminal, substituindo <exercise_test.py> pelo local/nome do ficheiro de testes:

$ python3 -m pytest -o markers=task <exercise_test.py>
==================== 7 passed in 0.08s ====================

Corrigir avisos

Se não usares a opção pytest -o markers=task no comando acima, é possível que recebas warnings sobre "unknown markers" ao correr um teste que usa a nossa nova sintaxe.

Para evitares escrever pytest -o markers=task em todos os testes que corres, podes usar um ficheiro de configuração pytest.ini, que pode ser descarregado a partir do nível superior do diretório do track de Python: pytest.ini.

Também podes criar o teu próprio ficheiro pytest.ini com o seguinte conteúdo:

[pytest]
markers =
    task: A concept exercise task.

Colocar este ficheiro no diretório raiz ou de trabalho dos exercícios do Exercism registará os marcadores e fará parar os avisos. Encontras mais informações sobre os marcadores do pytest na documentação do pytest sobre marcar funções de teste e na documentação do pytest sobre trabalhar com marcadores personalizados.

Encontras mais informações sobre a personalização de configurações do pytest na documentação do pytest sobre formatos de ficheiros de configuração

Falhas nos testes

Quando os testes falham, o pytest imprime no terminal o texto de cada teste falhado, juntamente com os valores return esperados e reais de cada um. Abaixo está um exemplo genérico de um teste falhado:

$(my_venv) python3 -m pytest -o markers=task <exercise_test.py>

=================== FAILURES ====================
______________ name_of_failed_test ______________
# Test code inside of <exercise_test.py> that failed.
...
E   TypeOfError: ReturnedValue != ExpectedValue

exercise_test.py:<line_of_failed_test>: TypeOfError
============ short test summary info ============
FAILED exercise_test.py::ExerciseTest::name_of_failed_test
========== 1 failed, 2 passed in 0.13s ==========

Argumentos extra

Se quiseres ser mesmo específico quanto ao que o pytest devolve no teu ecrã, aqui estão alguns argumentos de linha de comandos práticos que te permitem configurar o seu comportamento.

Devolver todos os detalhes [-v]

Adicionar a opção -v (verbose) devolve tanto informações sobre o ambiente como um resumo dos testes, além das falhas dos testes:

$(my_venv)  python3 -m pytest -o markers=task -v exercises/<exercise_name>/<test_file_test.py>

======================================== test session starts ===========================================
platform darwin -- Python 3.9.0, pytest-6.2.5, -- /usr/local/envs/my_env/bin/python3
cachedir: .pytest_cache
metadata: {'Python': '3.9.0', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1'}, 'Plugins': {'subtests': '0.5.0', 'pylint': '0.18.0'}
rootdir: /Users/<user>/exercism/python, configfile: pytest.ini
plugins: subtests-0.5.0, pylint-0.18.0

collected 5 items

exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_one FAILED                          [ 20%]
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_two FAILED
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_three PASSED                        [ 40%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_four FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_five PASSED                 [ 60%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_six FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_seven PASSED                [ 80%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_eight FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_nine PASSED                 [100%]

================================================ FAILURES ======================================================
# Failed tests are then individually printed out below

.......

Parar após a primeira falha [-x]

Usar a opção -x corre os testes normalmente, mas para a execução dos testes na primeira falha. Isto ajuda quando queres depurar uma única tarefa ou uma falha de teste de cada vez:

$(my_venv) python3 -m pytest -o markers=task -x exercises/<exercise_name>/<test_file_test.py>

=================== FAILURES ====================
_______________ example_test_foo ________________
...
...
============ short test summary info ============
FAILED example_test.py::ExampleTest::example_test_foo
!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!
========== 1 failed, 5 passed in 0.28s ==========

Testes falhados primeiro [--ff]

O plugin pytest-cache lembra-se de que testes falharam na última vez que correste o pytest, por isso usar a opção --ff diz ao pytest para correr primeiro os testes que falharam anteriormente e depois continuar com os restantes testes. Isto pode acelerar os teus testes se estiveres a fazer muitas correções pequenas em torno de uma tarefa ou de um conjunto de entradas específico.

$(my_venv) python3 -m pytest -o markers=task --ff <example_file_test.py>
==================== 7 passed in 503s ====================

Fluxo de trabalho recomendado

Recomendamos os seguintes comandos para tornar a tua depuração mais fácil e (possivelmente) mais rápida:

Primeiro, muda o teu diretório de trabalho para o diretório do exercício que queres testar:

$(my_venv) cd path/to/exercise

Depois, corre os testes juntamente com os argumentos explicados anteriormente -x e--ff:

$(my_venv) python3 -m pytest -o markers=task -x --ff <example_file_test.py>

Isto vai testar a tua solução. Quando o pytest encontra um teste falhado, o programa para e diz-te qual o teste que falhou. Quando fizeres correções e voltares a correr os testes, o pytest corre primeiro o teste que falhou anteriormente e depois continua com os restantes testes.

Usar o PDB, o depurador do Python, com o pytest

Se quiseres "depurar como um profissional", podes usar o argumento --pdb depois do comando pytest e entrar no depurador do Python incorporado, o PDB.

$(my_venv) python3 -m pytest -o markers=task -x --ff --pdb <example_file_test.py>
=============== 4 passed in 0.15s ===============

Quando um teste falha, entrar no PDB permite-te percorrer o teu código passo a passo, vendo o âmbito atual, bem como verificar o valor das variáveis e a assinatura de diferentes funções. Encontras mais detalhes sobre o módulo PDB na documentação do Python sobre o PDB. Além disso, a documentação do pytest sobre o PDB e este guia da Real Python são extremamente úteis.

Expandir o teu IDE

Se quiseres expandir o teu IDE com algumas ferramentas que te ajudarão a testar e a melhorar o teu código, consulta a página ferramentas. Aí exploramos vários IDEs, editores e algumas extensões úteis para análise de código e depuração.

Informações adicionais

Adicionar o Python ao teu PATH

Nota: Se instalaste o Python no Windows através do instalador da PSF, o comando será py em vez de python3.

Escrever python3 -m sempre que queres correr um módulo pode tornar-se um pouco irritante. Para evitar isso, podes adicionar a pasta Scripts da tua instalação do Python ao teu path. Se não sabes onde instalaste o Python, corre o seguinte comando no teu terminal:

$ python3 -c "import os, sys; print(os.path.dirname(sys.executable))"
<python_directory>

O diretório devolvido é onde está instalada a tua versão do Python atualmente ativa; nesta secção, é referido como <python_directory>.

Windows

Clica no botão Windows Start, procura Edit the system environment variables e prime Enter. Em seguida, prime Environment Variables...:

Carrega no botão azul, lol

Depois, encontra a variável Path nas tuas User variables, seleciona-a e clica em Edit...:

Selecionar a variável path

Depois, adiciona uma nova linha, como mostra a imagem, substituindo <python_directory> pelo diretório da tua instalação do Python:

Adicionar o python ao path

MacOS/Linux

O seguinte deve funcionar na maioria das distribuições de Linux e MacOS com uma shell bash. Os comandos podem variar consoante a distribuição de Linux e se usas uma shell fish ou zsh. Substitui <python_directory> pelo resultado de python3 -c "import os, sys; print(os.path.dirname(sys.executable))"

export PATH="$PATH:<python_directory>"