Exercício

Concluído

Neste exercício, você usará pytest com parametrizar para testar uma função. Em seguida, você atualiza uma classe de teste para usar uma instalação em vez de um método setup() e teardown(). Usar parametrizar e trabalhar com acessórios permite que você se torne mais flexível ao criar ou atualizar testes.

Etapa 1 – Adicionar um arquivo com testes para este exercício

  1. Crie um arquivo de teste chamado test_advanced.py e adicione o seguinte código:

    def str_to_bool(string):
        if string.lower() in ['yes', 'y', '1']:
            return True
        elif string.lower() in ['no', 'n', '0']:
            return False
    
    

    A função str_to_bool() aceita uma cadeia de caracteres como entrada e, dependendo de seu conteúdo, retorna um valor True ou False.

  2. No mesmo arquivo, anexe os testes para a função str_to_bool(). Use pytest.mark.parametrize() para testar todos os valores verdadeiros primeiro:

    import pytest 
    
    @pytest.mark.parametrize("string", ['Y', 'y', '1', 'YES'])
    def test_str_to_bool_true(string):
        assert str_to_bool(string) is True
    
  3. Em seguida, acrescente outro teste com os valores falsos:

    @pytest.mark.parametrize("string", ['N', 'n', '0', 'NO'])
    def test_str_to_bool_false(string):
        assert str_to_bool(string) is False
    

    Agora há dois testes que abrangem todas as entradas possíveis para os valores retornados True e False.

Observação

Não é comum colocar os testes no mesmo arquivo que o código real. Para simplificar, os exemplos neste exercício terão o código real no mesmo arquivo. Em projetos de Python do mundo real, você verá que os testes são separados por arquivos e diretórios do código que estão testando.

Etapa 2 – Executar os testes e explorar o relatório

Depois de adicionar os testes, a próxima etapa é executar pytest e inspecionar a saída. Use o sinalizador de detalhamento maior (-v) para ver todos os valores de entrada tratados como um teste separado.

$ pytest -v test_avanced.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 
rootdir: /private
collected 8 items

test_advanced.py::test_str_to_bool_true[Y] PASSED                        [ 12%]
test_advanced.py::test_str_to_bool_true[y] PASSED                        [ 25%]
test_advanced.py::test_str_to_bool_true[1] PASSED                        [ 37%]
test_advanced.py::test_str_to_bool_true[YES] PASSED                      [ 50%]
test_advanced.py::test_str_to_bool_false[N] PASSED                       [ 62%]
test_advanced.py::test_str_to_bool_false[n] PASSED                       [ 75%]
test_advanced.py::test_str_to_bool_false[0] PASSED                       [ 87%]
test_advanced.py::test_str_to_bool_false[NO] PASSED                      [100%]

============================== 8 passed in 0.01s ===============================

Embora você tenha escrito apenas duas funções de teste, pytest foi capaz de criar oito testes no total graças à função parametrize().

Etapa 3 – Mover um teste existente para um acessório

  1. Adicione um novo teste baseado em classe ao arquivo test_advanced.py. Esse teste deve usar uma função setup() e teardown() que cria um arquivo temporário com algum texto. Após cada teste, o arquivo é removido. Ele deverá ser parecido com:

    import os
    
    
    class TestFile:
    
        def setup(self):
            with open("/tmp/done", 'w') as _f:
                _f.write("1")
    
        def teardown(self):
            try:
                os.remove("/tmp/done")
            except OSError:
                pass
    
        def test_done_file(self):
            with open("/tmp/done") as _f:
                contents = _f.read()
            assert contents == "1"
    

    Essa classe de teste cria um arquivo, mas é problemática porque o caminho /tmp/ não tem garantia de estar presente em todos os sistemas.

  2. Crie um acessório que use o acessório pytesttmpdir() para gravar no arquivo e retornar o caminho:

    import pytest
    
    @pytest.fixture
    def tmpfile(tmpdir):
        def write():
            file = tmpdir.join("done")
            file.write("1")
            return file.strpath
        return write
    

    A instalação tmpfile() usa a instalação de tmpdir() do pytest, que garante um arquivo temporário válido que é limpo após a conclusão dos testes.

  3. Atualize a classe TestFile para que ela use o acessório em vez dos métodos auxiliares:

    class TestFile:
    
        def test_f(self, tmpfile):
            path = tmpfile()
            with open(path) as _f:
                contents = _f.read()
            assert contents == "1"
    

    Essa classe de teste agora pode garantir que um arquivo temporário seja criado e tenha o conteúdo adequado para que a declaração funcione.

Verificar seu trabalho

Agora, você deve ter um arquivo Python chamado test_advanced.py com o seguinte código:

  • Uma função str_to_bool() que aceita uma cadeia de caracteres e retorna um valor booliano dependendo do conteúdo da cadeia de caracteres.
  • Dois testes parametrizados para a função str_to_bool(), um que testa os valores True e o outro que testa os valores False.
  • Um dispositivo de pytest personalizado que usa a instalação de tmpdir() para criar um temporário feito arquivo com algum conteúdo.
  • Uma classe de teste que usa o acessório tmpfile() personalizado para criar o arquivo.

Todos os testes devem ser aprovados ao executá-los no terminal, sem erros.