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.
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.
PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...
$ 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
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>
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 ====================
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
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 ==========
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.
-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
.......
-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 ==========
--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 ====================
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.
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.
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.
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>.
Clica no botão Windows Start, procura Edit the system environment variables e prime Enter.
Em seguida, prime Environment Variables...:

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

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

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>"