Voltar Próximo 
Descobri que o Python Testing com pytest é um guia introdutório extremamente útil para o ambiente de testes pytest. Isso já me traz dividendos na minha empresa.
Chris barbeador
Vice-presidente de produto, tecnologia de insurreição

Os exemplos deste livro foram escritos usando Python 3.6 e pytest 3.2. O pytest 3.2 suporta Python 2.6, 2.7 e Python 3.3+.
O código-fonte do projeto Tarefas, bem como todos os testes mostrados neste livro, estão disponíveis no link na página da Web do livro em pragprog.com . Você não precisa fazer o download do código-fonte para entender o código de teste; o código de teste é apresentado de forma conveniente nos exemplos. Mas, para acompanhar as tarefas do projeto ou adaptar exemplos de teste para testar seu próprio projeto (suas mãos estão desamarradas!), Você deve acessar a página da Web do livro e fazer o download do trabalho. Lá, na página do livro, há um link para mensagens de errata e um fórum de discussão .
Sob o spoiler, há uma lista de artigos desta série.
Vamos lá
Este é o teste:
ch1 / test_one.py
def test_passing(): assert (1, 2, 3) == (1, 2, 3)
Aqui está o que parece na inicialização:
$ cd /path/to/code/ch1 $ pytest test_one.py ===================== test session starts ====================== collected 1 items test_one.py . =================== 1 passed in 0.01 seconds ===================

O ponto após test_one.py significa que um teste foi executado e passou. Se precisar de mais informações, você pode usar -v
ou --verbose
:
$ pytest -v test_one.py ===================== test session starts ====================== collected 1 items test_one.py::test_passing PASSED =================== 1 passed in 0.01 seconds ===================

Se você possui um terminal colorido, PASSA e a linha inferior é verde. Ótimo!
Este é um teste que falhou:
ch1 / test_two.py
def test_failing(): assert (1, 2, 3) == (3, 2, 1)
A maneira como o pytest mostra falhas de teste é uma das muitas razões pelas quais os desenvolvedores adoram o pytest. Vamos ver o que vem disso:
$ pytest test_two.py ===================== test session starts ====================== collected 1 items test_two.py F =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =================== 1 failed in 0.04 seconds ===================

Ótimo! O teste test_failing obtém sua seção para nos mostrar por que falhou.
E o pytest relata com precisão que a primeira falha: o índice 0 é uma incompatibilidade.
Grande parte desta mensagem é vermelha, o que a destaca (se você possui um terminal colorido).
Isso já é muita informação, mas há uma linha com uma dica que diz Use -v
para obter ainda mais descrições de diferenças.
Vamos ligar este -v
:
$ pytest -v test_two.py ===================== test session starts ====================== collected 1 items test_two.py::test_failing FAILED =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Full diff: E - (1, 2, 3) E ? ^ ^ E + (3, 2, 1) E ? ^ ^ test_two.py:2: AssertionError =================== 1 failed in 0.04 seconds ===================

Uau!
pytest adiciona o caractere de sinal de intercalação (^) para nos mostrar exatamente qual é a diferença.
Se você já está impressionado com a facilidade de escrever, ler e executar testes com pytest e como é fácil ler a saída para ver onde a falha ocorreu, bem ... você ainda não viu nada. De onde veio, existem muitos mais milagres. Fique e deixe-me mostrar por que acho que o pytest é absolutamente a melhor plataforma de teste.
No restante deste capítulo, você instalará o pytest, examinará as várias maneiras de executá-lo e seguirá algumas das opções de linha de comando mais usadas. Nos próximos capítulos, você aprenderá a escrever funções de teste que maximizam o poder do pytest, como inserir o código de instalação nas seções de configuração e desmontagem chamadas acessórios, e como usar acessórios e plugins para realmente sobrecarregar os testes de software.
Mas primeiro tenho que me desculpar. Desculpe testar a assert (1, 2, 3) == (3, 2, 1)
, é tão chato. Eu ouço ronco ?! Ninguém escreveria esse teste na vida real. Os testes de software consistem em código que verifica outro software que, infelizmente, nem sempre funciona de maneira positiva. E (1, 2, 3) == (1, 2, 3)
sempre funcionará. É por isso que não usaremos testes muito estúpidos como este no restante do livro. Consideraremos testes para um projeto de software real. Usaremos um projeto de Tarefas de amostra que requer código de teste. Espero que isso seja simples o suficiente para ser facilmente compreendido, mas não tão simples a ponto de ser entediante.
Outra aplicação útil dos testes de software é testar suas suposições sobre como o software em teste funciona, o que pode incluir testar sua compreensão de módulos e pacotes de terceiros e até criar estruturas de dados Python.
O projeto Tarefas usa a estrutura Tarefa, com base no método de fábrica nomeada de duplo, que faz parte da biblioteca padrão. A estrutura da tarefa é usada como uma estrutura de dados para transferir informações entre a interface do usuário e a API.
No restante deste capítulo, usarei a Tarefa para demonstrar como iniciar o pytest e usar algumas opções de linha de comando comumente usadas.
Aqui está a tarefa:
from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id'])
Antes de pularmos para os exemplos, vamos dar um passo atrás e falar sobre como obter o pytest e instalá-lo.
A função de fábrica namedtuple () existe desde o Python 2.6, mas ainda acho que muitos desenvolvedores de Python não sabem o quão legal é isso. Pelo menos, usar a tarefa para casos de teste será mais interessante que (1, 2, 3) == (1, 2, 3)
ou (1, 2)==3
.
Antes de passar para os exemplos, vamos dar um passo atrás e falar sobre onde obter o pytest e como instalá-lo.
Pytest de mineração
Sede da Pytest https://docs.pytest.org . Esta é a documentação oficial. Mas ele se espalha pelo PyPI (o índice do pacote Python) em https://pypi.python.org/pypi/pytest .
Como outros pacotes Python distribuídos através do PyPI, use pip para instalar o pytest no ambiente virtual usado para teste:
$ pip3 install -U virtualenv $ python3 -m virtualenv venv $ source venv/bin/activate $ pip install pytest
Se você não conhece o virtualenv ou o pip, apresentarei você. Consulte o Apêndice 1, “Ambientes virtuais”, na página 155 e o Apêndice 2, na página 159.
E o windows, python 2 e venv?
O exemplo virtualenv e pip deve funcionar em muitos sistemas POSIX, como Linux e macOS, bem como em muitas versões do Python, incluindo o Python 2.7.9 e posterior.
A fonte venv / bin / enable na linha não funcionará no Windows; use venv \ Scripts \ activ.bat .
Faça o seguinte:
C:\> pip3 install -U virtualenv C:\> python3 -m virtualenv venv C:\> venv\Scripts\activate.bat (venv) C:\> pip install pytest
Para o Python 3.6 e superior, você pode conviver com venv em vez de virtualenv e não precisa se preocupar em instalá-lo primeiro. Está incluído no Python 3.6 e posterior. No entanto, ouvi dizer que algumas plataformas ainda se comportam melhor com o virtualenv.
Executar pytest
$ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ...
Sem argumentos, o pytest examinará seu diretório atual e todos os subdiretórios dos arquivos de teste e executará o código de teste encontrado. Se você der ao pytest um nome de arquivo, um nome de diretório ou uma lista deles, eles serão encontrados lá, em vez do diretório atual. Cada diretório especificado na linha de comando é examinado recursivamente para encontrar o código de teste.
Por exemplo, vamos criar um subdiretório chamado task e começar com este arquivo de teste:
ch1 / tasks / test_three.py
""" Task.""" from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) Task.__new__.__defaults__ = (None, None, False, None) def test_defaults(): """ , .""" t1 = Task() t2 = Task(None, None, False, None) assert t1 == t2 def test_member_access(): """ .field () namedtuple.""" t = Task('buy milk', 'brian') assert t.summary == 'buy milk' assert t.owner == 'brian' assert (t.done, t.id) == (False, None)
O teste test_member_access () é para demonstrar como acessar membros por nome e não por índice, que é um dos principais motivos para usar nomeduplos.
Vamos colocar mais alguns testes em um segundo arquivo para demonstrar a funcionalidade _asdict () e _replace ()
Você pode usar __new __.__ defaults__
para criar objetos de tarefas sem especificar todos os campos. O teste test_defaults () tem como objetivo demonstrar e testar como os padrões funcionam.
O teste test_member_access()
deve demonstrar como acessar membros nomeados nd não pelo índice, que é um dos principais motivos para o uso dos nomeados.
Vamos adicionar mais alguns testes ao segundo arquivo para demonstrar as _asdict()
e _replace()
ch1 / tasks / test_four.py
""" Task.""" from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) Task.__new__.__defaults__ = (None, None, False, None) def test_asdict(): """_asdict() .""" t_task = Task('do something', 'okken', True, 21) t_dict = t_task._asdict() expected = {'summary': 'do something', 'owner': 'okken', 'done': True, 'id': 21} assert t_dict == expected def test_replace(): """ fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 10) assert t_after == t_expected
Para executar o pytest, você tem a opção de especificar arquivos e diretórios. Se você não especificar nenhum arquivo ou diretório, o pytest procurará testes no diretório de trabalho e nos subdiretórios atuais. Ele procura por arquivos começando com test_ ou terminando com _test. Se você executar pytest a partir do diretório ch1, sem comandos, executará testes para quatro arquivos:
$ cd /path/to/code/ch1 $ pytest ===================== test session starts ====================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError ============== 1 failed, 5 passed in 0.08 seconds ==============

Para executar apenas nossos novos testes de tarefas, você pode fornecer ao pytest todos os nomes de arquivos que deseja executar, ou um diretório, ou chamar pytest a partir do diretório em que nossos testes estão localizados:
$ pytest tasks/test_three.py tasks/test_four.py ===================== test session starts ====================== collected 4 items tasks/test_three.py .. tasks/test_four.py .. =================== 4 passed in 0.02 seconds =================== $ pytest tasks ===================== test session starts ====================== collected 4 items tasks/test_four.py .. tasks/test_three.py .. =================== 4 passed in 0.03 seconds =================== $ cd /path/to/code/ch1/tasks $ pytest ===================== test session starts ====================== collected 4 items test_four.py .. test_three.py .. =================== 4 passed in 0.02 seconds ===================

A parte de execução do pytest, onde o pytest passa e encontra os testes a serem executados, é chamado de descoberta de teste. O pytest conseguiu encontrar todos os testes que queríamos executar, porque os nomeamos de acordo com as convenções de nomenclatura do pytest.
A seguir, é apresentada uma breve visão geral das convenções de nomenclatura, para que seu código de teste possa ser detectado usando o pytest:
- Os arquivos de teste devem ter o nome
test_<something>.py
ou <something>_test.py
. - Os métodos e funções de teste devem ser chamados de
test_<something>
. - As classes de teste devem ser chamadas de
Test<Something>
.
Como nossos arquivos e funções de teste começam com test_
, tudo está bem conosco. Existem maneiras de alterar essas regras de descoberta se você tiver vários testes com nomes diferentes.
Vou abordar isso no Capítulo 6, “Configuração” na página 113.
Vamos dar uma olhada no resultado da execução de apenas um arquivo:
$ cd /path/to/code/ch1/tasks $ pytest test_three.py ================= test session starts ================== platform darwin -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0 rootdir: /path/to/code/ch1/tasks, inifile: collected 2 items test_three.py .. =============== 2 passed in 0.01 seconds ===============
O resultado nos diz um pouco.
===== sessão de teste inicia ====
.
O pytest fornece um separador elegante para iniciar uma sessão de teste. Uma sessão é uma única chamada pytest, incluindo todos os testes executados em vários diretórios. Essa definição de sessão se torna importante quando falo sobre a área da sessão em relação aos equipamentos pytest na definição da região do equipamento, na página 56.
Plataforma Darwin - no meu Mac. Em um computador Windows, a plataforma é diferente. A seguir, estão as versões do Python e pytest, bem como as dependências dos pacotes pytest. Tanto py quanto pluggy são pacotes desenvolvidos pela equipe pytest para ajudar na implementação do pytest.
rootdir: / caminho / para / código / ch1 / tarefas, inifile:
rootdir
é o diretório mais compartilhado entre todos os diretórios nos quais o código de teste é pesquisado. O inifile
(vazio aqui) lista os arquivos de configuração usados. Os arquivos de configuração podem ser pytest.ini
, tox.ini
ou setup.cfg
. Para obter mais informações sobre arquivos de configuração, consulte o Capítulo 6, “Configuração” na página 113.
coletou 2 itens
Essas são duas funções de teste em um arquivo.
test_three.py ..
test_three.py
mostra o arquivo de teste. Há uma linha para cada arquivo de teste. Dois pontos significam que os testes foram aprovados - um ponto para cada função ou método de teste. Os pontos são destinados apenas para passar nos testes. Falhas, erros, pulos, xfails e xpasses são indicados com F, E, s, xe X, respectivamente. Se você quiser ver mais pontos para passar nos testes, use a opção -v
ou --verbose
.
== 2 passaram em 0,01 segundos ==
Esta linha refere-se ao número de testes aprovados e ao tempo gasto em toda a sessão de teste. Se houver aprovação nos testes, o número de cada categoria também será mostrado aqui.
Um resultado do teste é a principal maneira pela qual um usuário que realiza um teste ou exibe resultados pode entender o que aconteceu durante o teste. No pytest, as funções de teste podem ter vários resultados diferentes, não apenas passar ou falhar. Aqui estão os possíveis resultados da função de teste:
- PASSADO (.): Teste concluído com sucesso.
- FAILED (F): Falha no teste (ou XPASS + estrito).
- SKIPPED (s): O teste foi pulado. Você pode forçar o pytest a pular o teste usando os
@pytest.mark.skip()
ou pytest.mark.skipif()
, discutidos na seção pulando testes, na página 34. - xfail (x): o teste não deveria ter passado, foi iniciado e falhou. Você pode forçar o pytest a instruir o teste a falhar usando o decorador
@pytest.mark.xfail()
, descrito nas marcações de teste como expectativa de falha, na página 37. - XPASS (X): O teste não deveria ter passado, foi iniciado e passou! ..
- ERRO (E): Ocorreu uma exceção fora da função de teste, no equipamento, discutido no capítulo 3, Acessórios pytest, na página 49, ou na função hook, discutida no capítulo 5, Plugins, na página 95.
Executando apenas um teste
Talvez a primeira coisa que você queira fazer depois de começar a escrever testes seja executar apenas um. Especifique o arquivo diretamente e inclua o nome ::test_name
:
$ cd /path/to/code/ch1 $ pytest -v tasks/test_four.py::test_asdict =================== test session starts =================== collected 3 items tasks/test_four.py::test_asdict PASSED ================ 1 passed in 0.01 seconds =================
Agora vamos ver algumas opções.
Usando Opções
Usamos a opção verbose, -v
ou --verbose
algumas vezes, mas há muitas outras opções que vale a pena conhecer. Não vamos usá-los todos neste livro, apenas alguns. Você pode visualizar a lista completa usando a opção pytest --help
.
Abaixo estão algumas opções que são muito úteis ao trabalhar com pytest. Esta não é uma lista completa, mas essas opções são suficientes para começar.
$ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ... subset of the list ... positional arguments: file_or_dir general: -k EXPRESSION only run tests which match the given substring expression. An expression is a python evaluatable expression where all names are substring-matched against test names and their parent classes. Example: -k 'test_method or test_other' matches all test functions and classes whose name contains 'test_method' or 'test_other', while -k 'not test_method' matches those that don't contain 'test_method' in their names. Additionally keywords are matched to classes and functions containing extra names in their 'extra_keyword_matches' set, as well as functions which have names assigned directly to them. -m MARKEXPR only run tests matching given mark expression. example: -m 'mark1 and not mark2'. --markers show markers (builtin, plugin and per-project ones). -x, --exitfirst exit instantly on first error or failed test. --maxfail=num exit after first num failures or errors. ... --capture=method per-test capturing method: one of fd|sys|no. -s shortcut for --capture=no. ... --lf, --last-failed rerun only the tests that failed at the last run (or all if none failed) --ff, --failed-first run all tests but run the last failures first. This may re-order tests and thus lead to repeated fixture setup/teardown ... reporting: -v, --verbose increase verbosity. -q, --quiet decrease verbosity. --verbosity=VERBOSE set verbosity ... -l, --showlocals show locals in tracebacks (disabled by default). --tb=style traceback print mode (auto/long/short/line/native/no). ... --durations=N show N slowest setup/test durations (N=0 for all). ... collection: --collect-only only collect tests, don't execute them. ... test session debugging and configuration: --basetemp=dir base temporary directory for this test run.(warning: this directory is removed if it exists) --version display pytest lib version and import information. -h, --help show help message and configuration info
--collect-only
A --collect-only
indica quais testes serão executados com os parâmetros e configurações fornecidos. É conveniente mostrar esse parâmetro primeiro, para que a saída possa ser usada como referência para outros exemplos. Se você iniciar no diretório ch1, deverá ver todas as funções de teste que procurou até agora neste capítulo:
$ cd /path/to/code/ch1 $ pytest --collect-only =================== test session starts =================== collected 6 items <Module 'test_one.py'> <Function 'test_passing'> <Module 'test_two.py'> <Function 'test_failing'> <Module 'tasks/test_four.py'> <Function 'test_asdict'> <Function 'test_replace'> <Module 'tasks/test_three.py'> <Function 'test_defaults'> <Function 'test_member_access'> ============== no tests ran in 0.03 seconds ===============
A --collect-only
é útil para verificar se as outras opções que selecionam testes antes da execução dos testes estão selecionadas corretamente. Vamos usá-lo novamente com -k
para mostrar como isso funciona.
-k EXPRESSÃO
A -k
permite que você use uma expressão para definir funções de teste.
Uma opção muito poderosa! Pode ser usado como um atalho para executar um teste separado, se o nome for exclusivo, ou executar um conjunto de testes que tenham um prefixo ou sufixo comum em seus nomes. Suponha que você queira executar os testes test_asdict()
e test_defaults()
. Você pode verificar o filtro com: --collect-only
:
$ cd /path/to/code/ch1 $ pytest -k "asdict or defaults" --collect-only =================== test session starts =================== collected 6 items <Module 'tasks/test_four.py'> <Function 'test_asdict'> <Module 'tasks/test_three.py'> <Function 'test_defaults'> =================== 4 tests deselected ==================== ============== 4 deselected in 0.03 seconds ===============
Sim! Isso é semelhante ao que precisamos. Agora você pode executá-los removendo --collect-only
:
$ pytest -k "asdict or defaults" =================== test session starts =================== collected 6 items tasks/test_four.py . tasks/test_three.py . =================== 4 tests deselected ==================== ========= 2 passed, 4 deselected in 0.03 seconds ==========
Opa! Apenas pontos. Então eles passaram. Mas eles eram os testes certos? Uma maneira de descobrir é usar -v
ou --verbose
:
$ pytest -v -k "asdict or defaults" =================== test session starts =================== collected 6 items tasks/test_four.py::test_asdict PASSED tasks/test_three.py::test_defaults PASSED =================== 4 tests deselected ==================== ========= 2 passed, 4 deselected in 0.02 seconds ==========
Sim! Esses foram os testes certos.
-m MARKLEX
Os marcadores são uma das melhores maneiras de marcar um subconjunto de recursos de teste para execução em conjunto. Como exemplo, uma maneira de executar test_replace()
e test_member_access()
, mesmo que estejam em arquivos separados, é marcá-los. Qualquer nome de marcador pode ser usado. Digamos que você queira usar run_these_please
. Observe os testes usando o decorador @pytest.mark.run_these_please
, assim:
import pytest ... @pytest.mark.run_these_please def test_member_access(): ...
Agora o mesmo para test_replace()
. Então você pode executar todos os testes com o mesmo marcador com pytest -m run_these_please
:
$ cd /path/to/code/ch1/tasks $ pytest -v -m run_these_please ================== test session starts =================== collected 4 items test_four.py::test_replace PASSED test_three.py::test_member_access PASSED =================== 2 tests deselected =================== ========= 2 passed, 2 deselected in 0.02 seconds =========
A expressão do marcador não precisa ser um único marcador. Você pode usar opções como -m "mark1 and mark2"
para testes com ambos os marcadores, -m "mark1 and not mark2"
para testes que possuem o rótulo 1, mas não o rótulo 2, -m "mark1 or mark2"
para testes com um de etc., discutirei com mais detalhes os marcadores nos Métodos de verificação de marcação, na página 31.
-x, --exitfirst
O comportamento normal do pytest é executar todos os testes que encontrar. Se a função de teste detectar uma falha de afirmação ou exceção , esse teste será interrompido e o teste falhará. E então pytest executa o próximo teste. Para a maior parte, é disso que você precisa. No entanto, especialmente ao depurar um problema, ele interfere imediatamente com toda a sessão de teste quando o teste não está correto. É isso que a opção -x
faz. Vamos tentar isso nos seis testes que temos no momento:
$ cd /path/to/code/ch1 $ pytest -x ====================== test session starts ==================== collected 6 items test_one.py . test_two.py F ============================ FAILURES ========================= __________________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =============== 1 failed, 1 passed in 0.38 seconds ============
Na parte superior da saída, você vê que todos os seis testes (ou "itens") foram coletados e, na linha inferior, você vê que um teste falhou e outro passou, e o pytest exibiu a linha "Interrompido" para nos informar que ele está parado. Sem -x
todos os seis testes seriam executados. Vamos repetir novamente sem -x
. Também usamos --tb=no
para desativar o rastreamento de pilha, pois você já o viu e não precisa vê-lo novamente:
$ cd /path/to/code/ch1 $ pytest --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============
, -x
, pytest test_two.py .
--maxfail=num
-x
. , , , --maxfail
, , . , . , --maxfail = 2
, , --maxfail = 1
, -x
:
$ cd /path/to/code/ch1 $ pytest --maxfail=2 --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.08 seconds ============ $ pytest --maxfail=1 --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F !!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!! =========== 1 failed, 1 passed in 0.19 seconds ============
E --tb=no
, .
-s and --capture=method
-s
— , stdout , . --capture=no
. , . , , , . -s
--capture=no
. print()
, .
, , -l/--showlocals
, , .
--capture=fd
--capture=sys
. — --capture=sys
sys.stdout/stderr
mem-. --capture=fd
1 2 .
sys
fd
. , , . -s
. , -s
, .
; . , , .
-lf, --last-failed
. --lf
:
, --tb
, , .
$ cd /path/to/code/ch1 $ pytest --lf =================== test session starts =================== run-last-failure: rerun last 1 failures collected 6 items test_two.py F ======================== FAILURES ========================= ______________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =================== 5 tests deselected ==================== ========= 1 failed, 5 deselected in 0.08 seconds ==========
–ff, --failed-first
--ff/--failed-first
, --last-failed
, , :
$ cd /path/to/code/ch1 $ pytest --ff --tb=no =================== test session starts =================== run-last-failure: rerun last 1 failures first collected 6 items test_two.py F test_one.py . tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============
test_failing()
test\_two.py
test\_one.py
. , test_failing()
, --ff
-v, --verbose
-v/--verbose
. , , .
, --ff
--tb=no
:
$ cd /path/to/code/ch1 $ pytest -v --ff --tb=no =================== test session starts =================== run-last-failure: rerun last 1 failures first collected 6 items test_two.py::test_failing FAILED test_one.py::test_passing PASSED tasks/test_four.py::test_asdict PASSED tasks/test_four.py::test_replace PASSED tasks/test_three.py::test_defaults PASSED tasks/test_three.py::test_member_access PASSED =========== 1 failed, 5 passed in 0.07 seconds ============

FAILED PASSED.
-q, --quiet
-q/--quiet
-v/--verbose
; . --tb=line
, .
- q
:
$ cd /path/to/code/ch1 $ pytest -q .F.... ======================== FAILURES ========================= ______________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Full diff: E - (1, 2, 3) E ? ^ ^ E + (3, 2, 1) E ? ^ ^ test_two.py:2: AssertionError 1 failed, 5 passed in 0.08 seconds
-q
, . -q
( --tb=no
), , .
-l, --showlocals
-l/--showlocals
tracebacks
.
. test_replace()
t_expected = Task('finish book', 'brian', True, 10)
em
t_expected = Task('finish book', 'brian', True, 11)
10 11 . . --l/--showlocals
:
$ cd /path/to/code/ch1 $ pytest -l tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= ______________________ test_replace _______________________ @pytest.mark.run_these_please def test_replace(): """replace() should change passed in fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 11) > assert t_after == t_expected E AssertionError: assert Task(summary=...e=True, id=10) == Task(summary='...e=True, id=11) E At index 3 diff: 10 != 11 E Use -v to get the full diff t_after = Task(summary='finish book', owner='brian', done=True, id=10) t_before = Task(summary='finish book', owner='brian', done=False, id=None) t_expected = Task(summary='finish book', owner='brian', done=True, id=11) tasks\test_four.py:28: AssertionError =========== 1 failed, 3 passed in 0.08 seconds ============

t_after
, t_before
t_expected
, assert-.
--tb=style
--tb=style
. pytest , , . tracebacks , , . --tb=style
. , , short, line no. short
assert E ; line
; no .
test_replace()
, , . --tb=no
$ cd /path/to/code/ch1 $ pytest --tb=no tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. =========== 1 failed, 3 passed in 0.04 seconds ============
--tb=line in many cases is enough to tell what's wrong. If you have a ton of failing tests, this option can help to show a pattern in the failures:
--tb=line
, , . , :
$ pytest --tb=line tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= /path/to/code/ch1/tasks/test_four.py:20: AssertionError: assert Task(summary=...e=True, id=10) == Task( summary='...e=True, id=11) =========== 1 failed, 3 passed in 0.05 seconds ============
verbose tracebacks --tb=short
:
$ pytest --tb=short tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= ______________________ test_replace _______________________ tasks/test_four.py:20: in test_replace assert t_after == t_expected E AssertionError: assert Task(summary=...e=True, id=10) == Task( summary='...e=True, id=11) E At index 3 diff: 10 != 11 E Use -v to get the full diff =========== 1 failed, 3 passed in 0.04 seconds ============
, , .
, .
pytest --tb=long
traceback. pytest --tb=auto
tracebacks , . . pytest --tb=native
traceback .
--durations=N
--durations=N
, . ; N tests/setups/teardowns . --durations=0
, .
, time.sleep(0.1)
. , :
$ cd /path/to/code/ch1 $ pytest --durations=3 tasks ================= test session starts ================= collected 4 items tasks/test_four.py .. tasks/test_three.py .. ============== slowest 3 test durations =============== 0.10s call tasks/test_four.py::test_replace 0.00s setup tasks/test_three.py::test_defaults 0.00s teardown tasks/test_three.py::test_member_access ============== 4 passed in 0.13 seconds
sleep , . : call(), (setup) (teardown). , , , , . 3, pytest Fixtures, . 49.
--version
--version
pytest , :
$ pytest --version This is pytest version 3.0.7, imported from /path/to/venv/lib/python3.5/site-packages/pytest.py
pytest , pytest site-packages .
-h, --help
-h/--help
, , pytest. , stock- pytest, , , .
-h
:
- : pytest [] [file_or_dir] [file_or_dir] [...]
- ,
- , ini , 6, , . 113
- , pytest ( 6, , . 113)
- , pytest
--markers
, 2, , . 23 - , pytest
--fixtures
, 3, pytest, . 49
:
(shown according to specified file_or_dir or current dir if not specified)
, , . , pytest conftest.py, - (hook functions), , .
pytest conftest.py . conftest.py ini, pytest.ini 6 «», 113.
Exercícios
, python -m virtualenv
python -m venv
. , , , , , . , . 1, , . 155, .
.
- $ source venv/bin/activate - $ deactivate On Windows: - C:\Users\okken\sandbox>venv\scripts\activate.bat - C:\Users\okken\sandbox>deactivate
pytest . . 2, pip, 159, . , pytest, , .
. , . pytest .
assert. assert something == something_else; , :
- assert 1 in [2, 3, 4]
- assert a < b
- assert 'fizz' not in 'fizzbuzz'
O que vem a seguir
Neste capítulo, vimos onde obter o pytest e várias maneiras de executá-lo. No entanto, não discutimos o que está incluído nas funções de teste. No próximo capítulo, consideraremos escrever funções de teste, parametrizá-las para que elas sejam chamadas com dados diferentes e agrupar testes em classes, módulos e pacotes.
Voltar Próximo 