python-testing-tests (3 Part Series)
1 Testando testes no Python – Parte 1: motivos e alternativas
2 Testando testes no Python – Parte 2: fixtures parametrizadas
3 Testando testes no Python – Parte 3: Pytest dentro do Pytest
Pessoas geralmente começam engatinhando, depois andam, evoluem para corrida, e algumas fazem coisas mais estranhas, como Parkour.
Pessoas Devs geralmente começam codando, depois testam, evoluem para o TDD, e algumas fazem coisas mais estranhas, como testes de testes.
Boas vindas! 🤩 Esse é o 3º artigo de uma curta série, contando um pouco mais sobre “testes de testes”. 🧪
Vou discutir as motivações, alternativas, e detalhar as formas que fazemos em projetos de Python na Trybe.
Retomando de onde paramos
No artigo anterior mostrei como foi construída a primeira solução para testes de mutações customizadas, utilizando uma fixture autouse
parametrizada com as mutações.
Mas havia uma limitação: nesse modelo a pessoa estudante precisa construir todos os seus testes dentro de uma função específica. É suficiente se queremos exercitar a criação de bons assert
s, mas limitante quando pensamos em fazer testes mais elaborados e melhor organizados.
Trocando de “função” para “módulo”
Logo de início, a ideia era que precisávamos parar de parametrizar uma função de testes (o que a fixture faz no exemplo do artigo anterior), e fazer a parametrização para um arquivo (módulo) inteiro. Ou seja, executar todo um arquivo de testes da pessoa estudante para cada mutação.
Assim a pessoa estudante poderia fazer, por exemplo, 5 funções de teste em um arquivo e, quando uma mutação for aplicada, pelo menos 1 das 5 funções de teste deve falhar. Se todos os testes passarem para uma das mutações, consideramos que ainda não foi atingida a qualidade que esperamos.
A missão era minha, e eu não fazia ideia de como implementar. Novamente entramos naquele ponto que não havia nada pronto ou óbvio para usarmos, e precisei partir para pesquisa e experimentação.
“Como rodar um arquivo de teste no Pytest?”
Essa provavelmente foi a primeira pesquisa que fiz no Google, esperando que surgisse alguma resposta para o que precisávamos. E não, obviamente não apareceu.
Ou será que apareceu?
As respostas para essa pesquisa são de conteúdos para iniciantes, e elas citam comandos básicos da CLI do Pytest:
python <span>-m</span> pytest tests/test_file.pypython <span>-m</span> pytest tests/test_file.pypython -m pytest tests/test_file.py
Enter fullscreen mode Exit fullscreen mode
E meu primeiro pensamento foi:
“Eu já sei disso! Me mostre algo que eu não sei! “
E logo em seguida:
“Calma, realmente é bem simples solicitar ao Pytest a execução de um arquivo completo de testes. Se eu conseguir fazer isso dentro de uma execução do Pytest que já está em curso, consigo usar a parametrização! Será que é possível? 🤔”
Resposta: Sim, é possível!
O Pytest é um módulo do Python como qualquer outro. Temos o costume de acioná-lo pela CLI, mas essa é apenas uma interface para um código “chamável” do Python. Na própria documentação do Pytest há a indicação de como executá-lo sem a CLI:
<span>import</span> <span>pytest</span><span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>()</span><span>import</span> <span>pytest</span> <span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>()</span>import pytest retcode = pytest.main()
Enter fullscreen mode Exit fullscreen mode
Um retcode
igual a 0
(zero) significa que os testes passaram, falharam caso contrário.
Executando o Pytest dentro do Pytest
Não parece uma ideia muito agradável, e até a documentação da ferramenta faz um alerta sobre isso:
[…] fazer multiplas chamadas a
pytest.main()
a partir do mesmo processo (para re-executar testes, por exemplo) não é recomendado.
Parece que escreveram isso especialmente pra mim! Mas sem ousadia nunca venceremos obstáculos, não é mesmo?
Brincadeiras a parte, seguimos entendendo que esse é um uso controlado e (até o momento) com complexidade moderada.
Ajustando o exemplo anterior
No exemplo do artigo anterior toda a configuração de mutações era feita nos arquivos tests/sorter_mutations.py
(definição das mutações) e tests/conftest.py
(parametrização e patch
das mutações), mas este 2º não será mais necessário.
Como nosso objetivo é somente ter um PASSED
caso a chamada do pytest.main()
falhe para todas as mutações, podemos abandonar a complexidade da fixture parametrizada com XFAIL
s e seguir com uma opção mais direta: uma função de teste parametrizada que fará a chamada ao pytest.main()
.
Isolando essa nova função de teste em um arquivo dedicado, teremos o seguinte:
Que traduzido para código, fica assim:
<span>from</span> <span>unittest.mock</span> <span>import</span> <span>patch</span><span>from</span> <span>tests</span> <span>import</span> <span>sorter_mutations</span><span>import</span> <span>pytest</span><span>mutated_functions</span> <span>=</span> <span>[</span><span>sorter_mutations</span><span>.</span><span>no_exception_mutation</span><span>,</span><span>sorter_mutations</span><span>.</span><span>slice_input_mutation</span><span>,</span><span>]</span><span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>mutated_functions</span><span>)</span><span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span><span>with</span> <span>patch</span><span>(</span><span>"</span><span>tests.test_sorter.sort_this_by</span><span>"</span><span>,</span> <span>mutation</span><span>):</span><span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>([</span><span>"</span><span>tests/test_sorter.py</span><span>"</span><span>])</span><span>assert</span> <span>retcode</span> <span>!=</span> <span>0</span><span>,</span> <span>"</span><span>Mutação deveria falhar</span><span>"</span><span>from</span> <span>unittest.mock</span> <span>import</span> <span>patch</span> <span>from</span> <span>tests</span> <span>import</span> <span>sorter_mutations</span> <span>import</span> <span>pytest</span> <span>mutated_functions</span> <span>=</span> <span>[</span> <span>sorter_mutations</span><span>.</span><span>no_exception_mutation</span><span>,</span> <span>sorter_mutations</span><span>.</span><span>slice_input_mutation</span><span>,</span> <span>]</span> <span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>mutated_functions</span><span>)</span> <span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span> <span>with</span> <span>patch</span><span>(</span><span>"</span><span>tests.test_sorter.sort_this_by</span><span>"</span><span>,</span> <span>mutation</span><span>):</span> <span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>([</span><span>"</span><span>tests/test_sorter.py</span><span>"</span><span>])</span> <span>assert</span> <span>retcode</span> <span>!=</span> <span>0</span><span>,</span> <span>"</span><span>Mutação deveria falhar</span><span>"</span>from unittest.mock import patch from tests import sorter_mutations import pytest mutated_functions = [ sorter_mutations.no_exception_mutation, sorter_mutations.slice_input_mutation, ] @pytest.mark.parametrize("mutation", mutated_functions) def test_mutations_for_test_module(mutation): with patch("tests.test_sorter.sort_this_by", mutation): retcode = pytest.main(["tests/test_sorter.py"]) assert retcode != 0, "Mutação deveria falhar"
Enter fullscreen mode Exit fullscreen mode
Dado que não melhoramos o último exemplo de “teste da pessoa estudante”, ao executar python -m pytest
teremos a saída semelhante a seguinte:
Os 2 PASSED
indicados na imagem são:
- A própria função da pessoa estudante sem a mutação aplicada
- O teste com a mutação
slice_input_mutation
, que falhou como esperado na chamadapytest.main(["tests/test_sorter.py"])
E, como imaginávamos, a chamada pytest.main
com a mutação no_exception_mutation
retornou 0
e por isso nosso assert
acusou um problema: “Mutação deveria falhar” (mas não falhou).
Melhorando a solução
Particularmente fiquei muito orgulhoso com essa solução! Mas há melhorias importante antes de chegarmos na versão disponibilizada para as turmas.
Ocultar logs excessivos
Quando executamos o Pytest internamente, ele se comporta de fato como uma nova execução, gerando todos os logs como esperado. Em nosso exemplo o terminal ficou poluído com logs equivalentes a 3 rodadas do Pytest, e isso não é bom para a experiência, além de confundir a pessoa estudante.
O Pytest possui algumas opções para reduzir a verbosidade de logs, mas sentimos que seria melhor ocultar completamente a saída das chamadas internas. Com 4 linhas podemos fazer a saída de um comando ser redirecionada para a “lixeira” /dev/null
:
<span>+import contextlib +import os </span><span>from unittest.mock import patch from tests import sorter_mutations import pytest </span><span> </span><span>mutated_functions = [ </span> sorter_mutations.no_exception_mutation,sorter_mutations.slice_input_mutation,]<span> </span>@pytest.mark.parametrize("mutation", mutated_functions)<span>def test_mutations_for_test_module(mutation): </span> with patch("tests.test_sorter.sort_this_by", mutation):<span>+ with open(os.devnull, "w") as student_output: + with contextlib.redirect_stdout(student_output): </span> retcode = pytest.main(["tests/test_sorter.py"])assert retcode != 0, "Mutação deveria falhar"<span> </span><span>+import contextlib +import os </span><span>from unittest.mock import patch from tests import sorter_mutations import pytest </span><span> </span><span>mutated_functions = [ </span> sorter_mutations.no_exception_mutation, sorter_mutations.slice_input_mutation, ] <span> </span>@pytest.mark.parametrize("mutation", mutated_functions) <span>def test_mutations_for_test_module(mutation): </span> with patch("tests.test_sorter.sort_this_by", mutation): <span>+ with open(os.devnull, "w") as student_output: + with contextlib.redirect_stdout(student_output): </span> retcode = pytest.main(["tests/test_sorter.py"]) assert retcode != 0, "Mutação deveria falhar" <span> </span>+import contextlib +import os from unittest.mock import patch from tests import sorter_mutations import pytest mutated_functions = [ sorter_mutations.no_exception_mutation, sorter_mutations.slice_input_mutation, ] @pytest.mark.parametrize("mutation", mutated_functions) def test_mutations_for_test_module(mutation): with patch("tests.test_sorter.sort_this_by", mutation): + with open(os.devnull, "w") as student_output: + with contextlib.redirect_stdout(student_output): retcode = pytest.main(["tests/test_sorter.py"]) assert retcode != 0, "Mutação deveria falhar"
Enter fullscreen mode Exit fullscreen mode
Mutações devem falhar and
Original deve passar
Tivemos um desafio semelhante na solução anterior usando a fixture: além de garantir que as mutações devem falhar, devemos garantir que o teste “normal” ou “original” deve passar.
Aqui novamente a biblioteca pytest-dependency
e o módulo inspect
entram como grandes amigos! Coletamos todas as funções de teste no arquivo tests/test_sorter.py
e as adicionamos como dependências para o novo teste test_mutations_for_test_module
.
Uma função que coleta todos os nodeid‘s de testes funcionais para um arquivo pode ser escrita assim:
<span>import</span> <span>inspect</span><span>from</span> <span>pathlib</span> <span>import</span> <span>Path</span><span>def</span> <span>get_test_functions_from</span><span>(</span><span>student_test_module</span><span>):</span><span>test_file_path</span> <span>=</span> <span>Path</span><span>(</span><span>student_test_module</span><span>.</span><span>__file__</span><span>).</span><span>relative_to</span><span>(</span><span>Path</span><span>.</span><span>cwd</span><span>())</span><span>return</span> <span>[</span><span>test_file_path</span> <span>+</span> <span>"</span><span>::</span><span>"</span> <span>+</span> <span>member</span><span>[</span><span>0</span><span>]</span><span>for</span> <span>member</span> <span>in</span> <span>inspect</span><span>.</span><span>getmembers</span><span>(</span><span>student_test_module</span><span>)</span><span>if</span> <span>inspect</span><span>.</span><span>isfunction</span><span>(</span><span>member</span><span>[</span><span>1</span><span>])</span> <span>and</span> <span>member</span><span>[</span><span>0</span><span>].</span><span>startswith</span><span>(</span><span>"</span><span>test_</span><span>"</span><span>)</span><span>]</span><span>import</span> <span>inspect</span> <span>from</span> <span>pathlib</span> <span>import</span> <span>Path</span> <span>def</span> <span>get_test_functions_from</span><span>(</span><span>student_test_module</span><span>):</span> <span>test_file_path</span> <span>=</span> <span>Path</span><span>(</span><span>student_test_module</span><span>.</span><span>__file__</span><span>).</span><span>relative_to</span><span>(</span><span>Path</span><span>.</span><span>cwd</span><span>())</span> <span>return</span> <span>[</span> <span>test_file_path</span> <span>+</span> <span>"</span><span>::</span><span>"</span> <span>+</span> <span>member</span><span>[</span><span>0</span><span>]</span> <span>for</span> <span>member</span> <span>in</span> <span>inspect</span><span>.</span><span>getmembers</span><span>(</span><span>student_test_module</span><span>)</span> <span>if</span> <span>inspect</span><span>.</span><span>isfunction</span><span>(</span><span>member</span><span>[</span><span>1</span><span>])</span> <span>and</span> <span>member</span><span>[</span><span>0</span><span>].</span><span>startswith</span><span>(</span><span>"</span><span>test_</span><span>"</span><span>)</span> <span>]</span>import inspect from pathlib import Path def get_test_functions_from(student_test_module): test_file_path = Path(student_test_module.__file__).relative_to(Path.cwd()) return [ test_file_path + "::" + member[0] for member in inspect.getmembers(student_test_module) if inspect.isfunction(member[1]) and member[0].startswith("test_") ]
Enter fullscreen mode Exit fullscreen mode
Nodeid é o identificador de um teste no Pytest, exigido pela pytest-dependency
. É o mesmo texto que aparece antes de PASSED
ou FAILED
quando executamos a CLI com -vv
. Exemplo:
Obs: Se a pessoa estudante utilizar parametrização em seus testes, essa função de coleta de nodeid‘s não será suficiente para a
pytest-dependency
funcionar corretamente. Por isso alteramos nosso fork novamente, garantindo a interpretação correta das dependências.
Nosso assert
, nossas regras
Já que foi necessário criar um assert
para garantir que o teste falhou para uma mutação, podemos aproveitar para criar uma mensagem de erro bem específica e didática. Porque só informar "Mutação deveria falhar"
se podemos chegar em algo como "Seus testes em '{arquivo}' deveriam falhar com a mutação '{mutação}' definida em '{arquivo de mutações}', mas passaram. Confira essa dica: {dica específica da mutação}"
?
Poderíamos ter um map/dicionário para definir a dica de cada mutação, mas escolhemos uma forma mais “preguiçosa”: a docstring da própria mutação. Um exemplo seria:
<span># ... </span><span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>mutated_functions</span><span>)</span><span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span><span>with</span> <span>patch</span><span>(</span><span>"</span><span>tests.test_sorter.sort_this_by</span><span>"</span><span>,</span> <span>mutation</span><span>):</span><span>with</span> <span>open</span><span>(</span><span>os</span><span>.</span><span>devnull</span><span>,</span> <span>"</span><span>w</span><span>"</span><span>)</span> <span>as</span> <span>student_output</span><span>:</span><span>with</span> <span>contextlib</span><span>.</span><span>redirect_stdout</span><span>(</span><span>student_output</span><span>):</span><span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>([</span><span>"</span><span>tests/test_sorter.py</span><span>"</span><span>])</span><span>assert</span> <span>retcode</span> <span>!=</span> <span>0</span><span>,</span> <span>(</span><span>"</span><span>Seus testes em </span><span>'</span><span>tests/test_sorter.py</span><span>'</span><span> deveriam falhar com a mutação,</span><span>"</span><span>f</span><span>"</span><span> mas passaram. Confira essa dica: </span><span>{</span><span>mutation</span><span>.</span><span>__doc__</span><span>}</span><span>"</span><span>)</span><span># ... </span> <span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>mutated_functions</span><span>)</span> <span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span> <span>with</span> <span>patch</span><span>(</span><span>"</span><span>tests.test_sorter.sort_this_by</span><span>"</span><span>,</span> <span>mutation</span><span>):</span> <span>with</span> <span>open</span><span>(</span><span>os</span><span>.</span><span>devnull</span><span>,</span> <span>"</span><span>w</span><span>"</span><span>)</span> <span>as</span> <span>student_output</span><span>:</span> <span>with</span> <span>contextlib</span><span>.</span><span>redirect_stdout</span><span>(</span><span>student_output</span><span>):</span> <span>retcode</span> <span>=</span> <span>pytest</span><span>.</span><span>main</span><span>([</span><span>"</span><span>tests/test_sorter.py</span><span>"</span><span>])</span> <span>assert</span> <span>retcode</span> <span>!=</span> <span>0</span><span>,</span> <span>(</span> <span>"</span><span>Seus testes em </span><span>'</span><span>tests/test_sorter.py</span><span>'</span><span> deveriam falhar com a mutação,</span><span>"</span> <span>f</span><span>"</span><span> mas passaram. Confira essa dica: </span><span>{</span><span>mutation</span><span>.</span><span>__doc__</span><span>}</span><span>"</span> <span>)</span># ... @pytest.mark.parametrize("mutation", mutated_functions) def test_mutations_for_test_module(mutation): with patch("tests.test_sorter.sort_this_by", mutation): with open(os.devnull, "w") as student_output: with contextlib.redirect_stdout(student_output): retcode = pytest.main(["tests/test_sorter.py"]) assert retcode != 0, ( "Seus testes em 'tests/test_sorter.py' deveriam falhar com a mutação," f" mas passaram. Confira essa dica: {mutation.__doc__}" )
Enter fullscreen mode Exit fullscreen mode
Primeiro a bagunça, depois a arrumação
Ufa… Muito código (e nem mostrei tudo) mas chegamos lá!
E nesse momento vem a dor de olhar todo aquele código criativo, mas ainda bagunçado. Só de olhar o “resultado final” já quero fugir de manutenções futuras, ainda mais pensando em múltiplos projetos e múltiplas turmas.
Por isso, aquela boa e velha refatoração sempre cai bem. Criando uma classe e algumas funções para isolar responsabilidades, temos um resultado mais palatável:
<span>from</span> <span>unittest.mock</span> <span>import</span> <span>patch</span><span>import</span> <span>pytest</span><span># Aproveitei nosso fork da pytest-dependency para # posicionar as funções de apoio </span><span>from</span> <span>pytest_dependency</span> <span>import</span> <span>(</span><span>assert_fails_with_mutation</span><span>,</span><span>get_skip_markers</span><span>,</span><span>get_test_assessment_configs</span><span>,</span><span>run_pytest_quietly</span><span>,</span><span>)</span><span>from</span> <span>src.sorter</span> <span>import</span> <span>sort_this_by</span><span>from</span> <span>tests</span> <span>import</span> <span>test_sorter</span><span>,</span> <span>sorter_mutations</span><span># TA_CFG será um objeto para guardar dados que queremos obter facilmente </span><span>TA_CFG</span> <span>=</span> <span>get_test_assessment_configs</span><span>(</span><span>target_asset</span><span>=</span><span>sort_this_by</span><span>,</span><span>mutations_module</span><span>=</span><span>sorter_mutations</span><span>,</span><span>student_test_module</span><span>=</span><span>test_sorter</span><span>,</span><span>)</span><span># Com essa configuração garantimos que só testaremos as mutações # caso a pessoa estudante tenha feito testes que passam. </span><span>pytestmark</span> <span>=</span> <span>get_skip_markers</span><span>(</span><span>TA_CFG</span><span>)</span><span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>TA_CFG</span><span>.</span><span>MUTATED_FUNCTIONS</span><span>)</span><span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span><span>with</span> <span>patch</span><span>(</span><span>TA_CFG</span><span>.</span><span>PATCH_TARGET</span><span>,</span> <span>mutation</span><span>):</span><span>return_code</span> <span>=</span> <span>run_pytest_quietly</span><span>([</span><span>TA_CFG</span><span>.</span><span>STUDENT_TEST_FILE_PATH</span><span>])</span><span>assert_fails_with_mutation</span><span>(</span><span>mutation</span><span>,</span> <span>return_code</span><span>,</span> <span>TA_CFG</span><span>)</span><span>from</span> <span>unittest.mock</span> <span>import</span> <span>patch</span> <span>import</span> <span>pytest</span> <span># Aproveitei nosso fork da pytest-dependency para # posicionar as funções de apoio </span><span>from</span> <span>pytest_dependency</span> <span>import</span> <span>(</span> <span>assert_fails_with_mutation</span><span>,</span> <span>get_skip_markers</span><span>,</span> <span>get_test_assessment_configs</span><span>,</span> <span>run_pytest_quietly</span><span>,</span> <span>)</span> <span>from</span> <span>src.sorter</span> <span>import</span> <span>sort_this_by</span> <span>from</span> <span>tests</span> <span>import</span> <span>test_sorter</span><span>,</span> <span>sorter_mutations</span> <span># TA_CFG será um objeto para guardar dados que queremos obter facilmente </span><span>TA_CFG</span> <span>=</span> <span>get_test_assessment_configs</span><span>(</span> <span>target_asset</span><span>=</span><span>sort_this_by</span><span>,</span> <span>mutations_module</span><span>=</span><span>sorter_mutations</span><span>,</span> <span>student_test_module</span><span>=</span><span>test_sorter</span><span>,</span> <span>)</span> <span># Com essa configuração garantimos que só testaremos as mutações # caso a pessoa estudante tenha feito testes que passam. </span><span>pytestmark</span> <span>=</span> <span>get_skip_markers</span><span>(</span><span>TA_CFG</span><span>)</span> <span>@pytest.mark.parametrize</span><span>(</span><span>"</span><span>mutation</span><span>"</span><span>,</span> <span>TA_CFG</span><span>.</span><span>MUTATED_FUNCTIONS</span><span>)</span> <span>def</span> <span>test_mutations_for_test_module</span><span>(</span><span>mutation</span><span>):</span> <span>with</span> <span>patch</span><span>(</span><span>TA_CFG</span><span>.</span><span>PATCH_TARGET</span><span>,</span> <span>mutation</span><span>):</span> <span>return_code</span> <span>=</span> <span>run_pytest_quietly</span><span>([</span><span>TA_CFG</span><span>.</span><span>STUDENT_TEST_FILE_PATH</span><span>])</span> <span>assert_fails_with_mutation</span><span>(</span><span>mutation</span><span>,</span> <span>return_code</span><span>,</span> <span>TA_CFG</span><span>)</span>from unittest.mock import patch import pytest # Aproveitei nosso fork da pytest-dependency para # posicionar as funções de apoio from pytest_dependency import ( assert_fails_with_mutation, get_skip_markers, get_test_assessment_configs, run_pytest_quietly, ) from src.sorter import sort_this_by from tests import test_sorter, sorter_mutations # TA_CFG será um objeto para guardar dados que queremos obter facilmente TA_CFG = get_test_assessment_configs( target_asset=sort_this_by, mutations_module=sorter_mutations, student_test_module=test_sorter, ) # Com essa configuração garantimos que só testaremos as mutações # caso a pessoa estudante tenha feito testes que passam. pytestmark = get_skip_markers(TA_CFG) @pytest.mark.parametrize("mutation", TA_CFG.MUTATED_FUNCTIONS) def test_mutations_for_test_module(mutation): with patch(TA_CFG.PATCH_TARGET, mutation): return_code = run_pytest_quietly([TA_CFG.STUDENT_TEST_FILE_PATH]) assert_fails_with_mutation(mutation, return_code, TA_CFG)
Enter fullscreen mode Exit fullscreen mode
Muitas alegrias, até que…
Esse formato funcionou muito bem para nossos projetos sobre POO, Raspagem de Dados, Algoritmos e Estruturas de Dados, Flask… até que começamos a ensinar Django.
Django é um framework incrível, mas ele abstrai muitos detalhes de implementação. Isso acontece principalmente em relação a comunicação com o banco de dados, e é mais “agravante” quando testes acessam o banco.
Tentamos bastante até entender que não seria viável, com a solução descrita nesse artigo, testar testes que precisavam acessar o banco de dados de uma aplicação Django. Precisávamos voltar ao passo da pesquisa, com boas doses de ousadia, criatividade e paciência.
Vou ser sincero aqui: ainda não tenho a resposta final! Fizemos uma prova de conceito com hooks do Pytest que parece promissora, mas ainda não chegamos lá.
Por isso, o próximo artigo pode demorar um pouco a sair, mas já estou ansioso para esse momento!
python-testing-tests (3 Part Series)
1 Testando testes no Python – Parte 1: motivos e alternativas
2 Testando testes no Python – Parte 2: fixtures parametrizadas
3 Testando testes no Python – Parte 3: Pytest dentro do Pytest
原文链接:Testando testes no Python – Parte 3: Pytest dentro do Pytest
暂无评论内容