Teste de Python com pytest. Configuração, CAPÍTULO 6

Voltar PrĂłximo


Neste capítulo, examinaremos os arquivos de configuração que afetam o pytest, discutiremos como o pytest altera seu comportamento com base neles e faremos algumas alterações nos arquivos de configuração do projeto Tarefas.



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.



Configuração


Até agora neste livro, falei sobre vários arquivos que não são de teste que afetam o pytest principalmente de passagem, com a exceção do conftest.py, que abordamos com mais detalhes no Capítulo 5, Plugins, na página 95. Neste capítulo, examinaremos os arquivos de configuração, que afetam o pytest, discuta como o pytest altera seu comportamento com base neles e faça algumas alterações nos arquivos de configuração do projeto Tarefas.


Noções básicas sobre arquivos de configuração pytest


Antes de dizer como vocĂŞ pode alterar o comportamento padrĂŁo no pytest, vamos examinar todos os arquivos que nĂŁo sĂŁo de teste no pytest e, em particular, quem deve cuidar deles.


VocĂŞ deve saber o seguinte:


  • pytest.ini : este Ă© o principal arquivo de configuração do Pytest que permite alterar o comportamento padrĂŁo. Como vocĂŞ pode fazer algumas alterações na configuração, a maior parte deste capĂ­tulo Ă© dedicada Ă s configurações que vocĂŞ pode fazer no pytest.ini .
  • conftest.py : Este Ă© um plug-in local que permite conectar funções e acessĂłrios de gancho ao diretĂłrio em que o arquivo conftest.py e a todos os seus subdiretĂłrios. O arquivo conftest.py descrito no capĂ­tulo 5 “Plugins” na página 95.
  • __init__.py : quando colocado em cada subdiretĂłrio de teste, esse arquivo permite que vocĂŞ tenha nomes de arquivos de teste idĂŞnticos em vários diretĂłrios de teste. Veremos um exemplo do que poderia dar errado sem os arquivos __init__.py nos diretĂłrios de teste no artigo “Evitando colisões de nomes de arquivos” na página 120.

Se você usa tox, estará interessado em:


  • tox.ini : esse arquivo Ă© semelhante ao pytest.ini , mas para o tox . No entanto, vocĂŞ pode colocar sua configuração pytest vez de ter o arquivo tox.ini e o arquivo pytest.ini , economizando um arquivo de configuração. O Tox Ă© discutido no capĂ­tulo 7, “Usando o pytest com outras ferramentas”, na página 125.

Se você deseja distribuir um pacote Python (por exemplo, Tarefas), este arquivo será do seu interesse:


  • setup.cfg : tambĂ©m Ă© um arquivo INI que afeta o comportamento do setup.py . VocĂŞ pode adicionar algumas linhas ao setup.py para executar o python setup.py test e executar todos os seus testes do pytest. Se vocĂŞ estiver distribuindo o pacote, vocĂŞ já pode ter o arquivo setup.cfg e pode usá-lo para armazenar a configuração do Pytest. VocĂŞ verá como isso Ă© feito no ApĂŞndice 4, “Empacotando e distribuindo projetos Python” na página 175.

Não importa em qual arquivo você colocou a configuração pytest, o formato será basicamente o mesmo.


Para pytest.ini :


ch6 / format / pytest.ini

 [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

Para tox.ini :


ch6 / formato / tox.ini

 ... tox specific stuff ... [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

Para setup.cfg :


ch6 / format / setup.cfg

 ... packaging specific stuff ... [tool:pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

A única diferença é que o cabeçalho da seção setup.cfg é [tool:pytest] vez de [pytest] .


Listar as opções válidas de arquivo ini com pytest –help


Você pode obter uma lista de todos os parâmetros válidos para pytest.ini em pytest --help :


 $ pytest --help ... [pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found: markers (linelist) markers for test functions empty_parameter_set_mark (string) default marker for empty parametersets norecursedirs (args) directory patterns to avoid for recursion testpaths (args) directories to search for tests when no files or directories are given in the command line. console_output_style (string) console output: classic or with additional progress information (classic|progress). usefixtures (args) list of default fixtures to be used with this project python_files (args) glob-style file patterns for Python test module discovery python_classes (args) prefixes or glob names for Python test class discovery python_functions (args) prefixes or glob names for Python test function and method discovery xfail_strict (bool) default for the strict parameter of xfail markers when not given explicitly (default: False) junit_suite_name (string) Test suite name for JUnit report junit_logging (string) Write captured log messages to JUnit report: one of no|system-out|system-err doctest_optionflags (args) option flags for doctests doctest_encoding (string) encoding used for doctest files cache_dir (string) cache directory path. filterwarnings (linelist) Each line specifies a pattern for warnings.filterwarnings. Processed after -W and --pythonwarnings. log_print (bool) default value for --no-print-logs log_level (string) default value for --log-level log_format (string) default value for --log-format log_date_format (string) default value for --log-date-format log_cli (bool) enable log display during test run (also known as "live logging"). log_cli_level (string) default value for --log-cli-level log_cli_format (string) default value for --log-cli-format log_cli_date_format (string) default value for --log-cli-date-format log_file (string) default value for --log-file log_file_level (string) default value for --log-file-level log_file_format (string) default value for --log-file-format log_file_date_format (string) default value for --log-file-date-format addopts (args) extra command line options minversion (string) minimally required pytest version xvfb_width (string) Width of the Xvfb display xvfb_height (string) Height of the Xvfb display xvfb_colordepth (string) Color depth of the Xvfb display xvfb_args (args) Additional arguments for Xvfb xvfb_xauth (bool) Generate an Xauthority token for Xvfb. Needs xauth. ... 

Você verá todas essas configurações neste capítulo, com exceção dos doctest_optionflags , discutidos no Capítulo 7, “Usando o pytest com outras ferramentas” na página 125.


Os plugins podem adicionar opções de arquivo ini


A lista anterior de configurações não é uma constante. Para plugins (e arquivos conftest.py), é possível adicionar opções de arquivos ini. As opções adicionadas também serão adicionadas à saída do comando pytest --help.
Agora, vejamos algumas mudanças na configuração que podemos fazer usando as configurações internas do arquivo .ini disponíveis no core pytest.


Alterar opções de linha de comando padrão


Você já usou algumas opções de linha de comando para o pytest , como -v/--verbose para saída detalhada -l/--showlocals para visualizar variáveis ​​locais com um rastreamento de pilha para testes com falha. Você pode achar que sempre usa algumas dessas options—or prefere usá- them—for a project . Se você instalar addopts no pytest.ini para os parâmetros necessários, não precisará mais inseri-los. Aqui está um conjunto que eu gosto:


 [pytest] addopts = -rsxX -l --tb=short --strict 

A -rsxX permite que o pytest relate os motivos de todos os testes skipped , xfailed ou xpassed . A opção -l permite ao pytest exibir um rastreamento de pilha para variáveis ​​locais no caso de cada falha. --tb=short removerá a maior parte do rastreamento de pilha. No entanto, ele deixará o arquivo e o número da linha. A --strict proíbe o uso de tokens se eles não estiverem registrados no arquivo de configuração. Você verá como fazer isso na próxima seção.


Registro de marcador para evitar erros de digitação de marcadores


Marcadores personalizados, conforme descrito em “Funções de teste de rotulagem” na página 31, são ótimos para permitir que você marque um subconjunto de testes para executar com um marcador específico. No entanto, é muito fácil cometer um erro no marcador e, finalmente, alguns testes são marcados com @pytest.mark.smoke e outros são marcados com @pytest.mark.somke . Por padrão, isso não é um erro. O pytest apenas pensa que você criou dois marcadores. No entanto, isso pode ser corrigido registrando tokens no pytest.ini, como por exemplo:


 [pytest] ... markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() ... 

Ao registrar esses marcadores, agora você também pode vê-los com pytest --markers com suas descrições:


 $ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --markers @pytest.mark.smoke: Run the smoke test test functions @pytest.mark.get: Run the test functions that test tasks.get() @pytest.mark.skip(reason=None): skip the ... ... 

Se os marcadores não estiverem registrados, eles não aparecerão na lista --markers . Quando eles são registrados, eles são exibidos na lista e, se você usar --strict , todos os tokens com erros ou não registrados serão exibidos como erros. A única diferença entre ch6/a/tasks_proj e ch6/b/tasks_proj é o conteúdo do arquivo pytest.ini. Em ch6/a vazio. Vamos tentar executar os testes sem registrar nenhum marcador:


 $ cd /path/to/code/ch6/a/tasks_proj/tests $ pytest --strict --tb=line ============================= test session starts ============================= collected 45 items / 2 errors =================================== ERRORS ==================================== ______________________ ERROR collecting func/test_add.py ______________________ 'smoke' not a registered marker ________________ ERROR collecting func/test_api_exceptions.py _________________ 'smoke' not a registered marker !!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 2 error in 1.10 seconds =========================== 

Se você usa marcadores no pytest.ini para registrar seus marcadores, você também pode adicionar --strict aos seus addopts enquanto está nisso. Você me agradecerá mais tarde. Vamos em frente e adicione um arquivo pytest.ini ao projeto de tarefas:


Se você usar marcadores no pytest.ini para registrar seus marcadores, também poderá adicionar --strict aos seus addopts . Você me agradecerá mais tarde. Vamos continuar e adicionar o arquivo pytest.ini ao projeto da tarefa:


Se você usar tokens no pytest.ini para registrar tokens, também poderá adicionar --strict aos já existentes com addopts . Legal ?! pytest.ini agradecimentos e adicione o arquivo pytest.ini ao projeto de tasks :


ch6 / b / tasks_proj / tests / pytest.ini

 [pytest] addopts = -rsxX -l --tb=short --strict markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() 

Aqui está a combinação de sinalizadores preferida por padrão:


  • -rsxX para -rsxX quais testes foram ignorados, xfailed ou xpassed,
  • --tb = short de um rastreamento mais curto de falhas,
  • --strict a permitir apenas tokens declarados.
    E uma lista de marcadores para o projeto.

Isso deve nos permitir realizar testes, incluindo testes de fumaça:


 $ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --strict -m smoke ===================== test session starts ====================== collected 57 items func/test_add.py . func/test_api_exceptions.py .. ===================== 54 tests deselected ====================== =========== 3 passed, 54 deselected in 0.06 seconds ============ 

Requisito mĂ­nimo de Pytest


O parâmetro minversion permite especificar a versão mínima do pytest esperada para os testes. Por exemplo, pretendia usar approx() ao testar números de ponto flutuante para determinar a igualdade "razoavelmente próxima" nos testes. Mas essa função não foi introduzida no pytest até a versão 3.0. Para evitar confusão, adiciono o seguinte aos projetos que usam approx() :


 [pytest] minversion = 3.0 

Portanto, se alguém tentar executar testes usando uma versão mais antiga do pytest, uma mensagem de erro será exibida.


Pare de pytest de procurar nos lugares errados


Você sabia que uma das definições de "recurse" é jurar duas vezes no seu próprio código? Bem, não. De fato, isso significa contabilizar subdiretórios. O pytest permitirá a detecção de testes examinando recursivamente vários diretórios. Mas existem alguns diretórios que você deseja excluir da exibição do pytest.


O valor padrão para norecurse é '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' É um bom motivo para nomear seu ambiente virtual como '.venv', porque todos os diretórios que começam com um ponto não estarão visíveis.


No caso do projeto Tarefas, o src não fará mal em especificar, porque pesquisar em arquivos de teste usando pytest será uma perda de tempo.


 [pytest] norecursedirs = .* venv src *.egg dist build 

Ao substituir um parâmetro que já possui um valor útil, como esse parâmetro, é útil saber quais são os valores padrão e retornar os que você precisa, como fiz no código anterior com a *.egg dist build .
norecursedirs Ă© um tipo de consequĂŞncia para os caminhos de teste, entĂŁo vamos dar uma olhada nisso mais tarde.


especificação da árvore de diretórios de teste


Enquanto norecursedirs diz ao pytest onde procurar, testpaths diz ao pytest onde procurar. testspaths é uma lista de diretórios relativos ao diretório raiz para encontrar testes. É usado apenas se o diretório, arquivo ou nodeid não nodeid especificado como argumento.


Suponha que, para um projeto de Tasks , colocamos pytest.ini no diretĂłrio tasks_proj , em vez de testes:


 \code\tasks_proj>tree/f . │ pytest.ini │ ├───src │ └───tasks │ api.py │ ... │ └───tests │ conftest.py │ pytest.ini │ ├───func │ test_add.py │ ... │ ├───unit │ test_task.py │ __init__.py │ ... 

EntĂŁo, pode fazer sentido colocar os testes nos testpaths :


 [pytest] testpaths = tests 

Agora, se você executar pytest a partir do diretório tasks_proj, o pytest pesquisará apenas em tasks_proj/tests . O problema aqui é que, durante o desenvolvimento e a depuração de testes, eu frequentemente itero no diretório de teste, para que eu possa testar facilmente um subdiretório ou arquivo sem especificar o caminho inteiro. Portanto, essa opção me ajuda um pouco nos testes interativos.


No entanto, é ótimo para testes em execução a partir de um servidor de integração contínua ou tox. Nesses casos, você sabe que o diretório raiz será corrigido e poderá listar os diretórios relativos a esse diretório raiz fixo. Também são esses os casos em que você realmente deseja reduzir o tempo de teste, portanto, é ótimo se livrar da pesquisa de testes.


À primeira vista, pode parecer bobagem usar os caminhos de teste e os norecursedirs ao mesmo tempo. No entanto, como você já viu, os caminhos de teste ajudam pouco nos testes interativos de diferentes partes do sistema de arquivos. Nesses casos, os norecursedirs podem ajudar. Além disso, se você tiver diretórios de teste que não contenham testes, poderá usar norecursedirs para evitá-los. Mas, na verdade, qual é o sentido de colocar diretórios extras em testes que não têm testes?


Alterando regras de detecção de teste


O pytest encontra testes para executar com base em regras específicas de descoberta de testes. Regras padrão de detecção de teste:


• Comece com um ou mais diretórios. Você pode especificar os nomes de arquivos ou diretórios na linha de comando. Se você não especificou nada, o diretório atual é usado.
• Pesquise o catálogo e todos os seus subdiretórios por módulos de teste.
• Um módulo de teste é um arquivo com um nome semelhante a test_*.py ou *_test.py .
• Procure nos módulos de teste as funções que começam com o teste .
Procure classes que começam com Teste. Procure métodos nas classes que começam com `test , init` .


Essas são regras de detecção padrão; No entanto, você pode alterá-los.


python_classes


A regra usual para encontrar testes para pytest e classes é considerar uma classe como uma classe de teste em potencial se iniciar com Test* . A classe também não pode ter o método __init__() . Mas e se quisermos nomear nossas classes de teste como <something>Test ou <something>Suite ? É aqui que o python_classes entra:


 [pytest] python_classes = *Test Test* *Suite 

Isso nos permite nomear as classes assim:


 class DeleteSuite(): def test_delete_1(): ... def test_delete_2(): ... .... 

python_files


Como pytest_classes , python_files modifica a regra de detecção de teste padrão, que consiste em encontrar arquivos que começam com test_* ou possuem *_test no final.
Suponha que você tenha uma estrutura de teste personalizada na qual nomeou todos os seus arquivos de teste como check_<something>.py . Parece razoável. Em vez de renomear todos os seus arquivos, basta adicionar uma linha ao pytest.ini seguinte maneira:


 [pytest] python_files = test_* *_test check_* 

Muito simples Agora você pode transferir gradualmente a convenção de nomenclatura, se desejar, ou simplesmente deixá-la como check_* .


python_functions


python_functions atua como as duas configurações anteriores, mas para funções de teste e nomes de métodos. O valor padrão é test_* . E para adicionar check_* adivinhou - faça o seguinte:


 [pytest] python_functions = test_* check_* 

As pytest nomenclatura pytest não parecem tão restritivas, não é? Portanto, se você não gosta da convenção de nomenclatura padrão, basta alterá-la. No entanto, peço que você tenha uma razão mais convincente para tais decisões. Migrar centenas de arquivos de teste é definitivamente um bom motivo.


Proibição XPASS


Definir xfail_strict = true significa que os testes marcados com @pytest.mark.xfail não são reconhecidos como causadores do erro. Eu acho que essa configuração sempre deve ser. Para obter mais informações sobre o token xfail consulte “Marcando testes aguardando falha” na página 37.


Evitar conflitos de nome de arquivo


A utilidade de ter o arquivo __init__.py em cada subdiretório de teste do projeto me confundiu bastante. No entanto, a diferença entre tê-los ou não é simples. Se você tiver arquivos __init__.py em todos os seus subdiretórios de teste, poderá ter o mesmo nome de arquivo de teste em vários diretórios. E se não, então isso não vai funcionar.


Aqui está um exemplo. Os diretórios b têm o arquivo test_foo.py . Não importa o que esses arquivos contêm, mas, para este exemplo, eles se parecem com isso:


ch6 / dups / a / test_foo.py
 def test_a(): pass 


ch6 / dups / b / test_foo.py
 def test_b(): pass 

Com esta estrutura de diretĂłrios:


 dups ├── a │ └── test_foo.py └── b └── test_foo.py 

Esses arquivos nem têm o mesmo conteúdo, mas os testes estão corrompidos. Você poderá executá-los separadamente, mas não há como executar o dups diretório dups :


 $ cd /path/to/code/ch6/dups $ pytest a ============================= test session starts ============================= collected 1 item a\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest b ============================= test session starts ============================= collected 1 item b\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest ============================= test session starts ============================= collected 1 item / 1 errors =================================== ERRORS ==================================== _______________________ ERROR collecting b/test_foo.py ________________________ import file mismatch: imported module 'test_foo' has this __file__ attribute: /path/to/code/ch6/dups/a/test_foo.py which is not the same as the test file we want to collect: /path/to/code/ch6/dups/b/test_foo.py HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules !!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 1 error in 0.34 seconds =========================== 

Nada está claro!
Esta mensagem de erro nĂŁo indica o que deu errado.


Para corrigir esse teste, basta adicionar o arquivo __init__.py vazio aos subdiretórios. Aqui está um exemplo do diretório dups_fixed com os mesmos nomes de arquivos duplicados, mas com os arquivos __init__.py adicionados:


 dups_fixed/ ├── a │ ├── __init__.py │ └── test_foo.py └── b ├── __init__.py └── test_foo.py 

Agora vamos tentar novamente a partir do nĂ­vel superior em dups_fixed :


 $ cd /path/to/code/ch6/ch6/dups_fixed/ $ pytest ============================= test session starts ============================= collected 2 items a\test_foo.py . b\test_foo.py . ========================== 2 passed in 0.15 seconds =========================== 

Então será melhor.


Obviamente, você pode se convencer de que nunca terá nomes de arquivos duplicados, o que não importa. Tudo, como, é normal. Mas os projetos estão crescendo e os catálogos de testes estão crescendo, e você definitivamente quer esperar que isso aconteça antes de cuidar disso? Eu digo, basta colocar esses arquivos lá. Crie um hábito e não se preocupe com isso novamente.


ExercĂ­cios


No Capítulo 5, Plugins, na página 95, você criou um plugin chamado pytest-nice que incluía uma opção de linha de comando --nice. Vamos estender isso para incluir uma opção pytest.ini chamada nice.


No Capítulo 5, “Plugins” na página 95, você criou um plugin chamado pytest-nice que inclui a --nice linha de comando --nice . Vamos expandir isso para incluir a opção pytest.ini chamada nice .


  1. Adicione a seguinte linha à função de gancho pytest_addoption pytest_nice.py : parser.addini('nice', type='bool', help='Turn failures into opportunities.')
  2. Os lugares no plugin que usam getoption() também terão que chamar getini('nice') . Faça essas alterações.
  3. Verifique isso manualmente adicionando nice ao arquivo pytest.ini .
  4. Não se esqueça dos testes de plugins. Adicione um teste para verificar se o nice parâmetro do pytest.ini funcionando corretamente.
  5. Adicione testes ao diretĂłrio do plug-in. VocĂŞ precisa encontrar alguns recursos adicionais do Pytester .

O que vem a seguir


Embora o pytest seja extremamente poderoso por si só - especialmente com plugins -, ele também se integra bem a outras ferramentas de desenvolvimento e teste de software. No próximo capítulo, examinaremos o uso do pytest em conjunto com outras poderosas ferramentas de teste.


Voltar PrĂłximo

Source: https://habr.com/ru/post/pt448796/


All Articles