Ejercicio

Completado

En este ejercicio, usarás pytest con parametrize para probar una función. A continuación, actualizará una clase de prueba para usar un accesorio de prueba en lugar de un método setup() y teardown(). El uso de parametrize y el trabajo con accesorios de prueba le permitirá ser más flexible al crear o actualizar las pruebas.

Paso 1: Adición de un archivo con pruebas para este ejercicio

  1. Cree un nuevo archivo de prueba denominado test_advanced.py y agregue el código siguiente:

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

    La función str_to_bool() acepta una cadena como entrada y, en función de su contenido, devolverá un valor True o False.

  2. En el mismo archivo, anexe las pruebas para la función str_to_bool(). Use pytest.mark.parametrize() para probar primero todos los valores true:

    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. A continuación, anexe otra prueba con los valores false:

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

    Ahora hay dos pruebas que cubren todas las entradas posibles para los valores devueltos True y False.

Nota

No es habitual tener pruebas dentro del mismo archivo como código real. Por motivos de simplicidad, los ejemplos de este ejercicio tendrán código real en el mismo archivo. En los proyectos de Python reales, descubrirá que las pruebas están separadas por archivos y directorios del código que está probando.

Paso 2: Ejecución de las pruebas y exploración del informe

Después de agregar pruebas, el siguiente paso consiste en ejecutar pytest e inspeccionar la salida. Use la marca de detalle aumentada (-v) para que pueda ver que todos los valores de entrada se tratan como una prueba independiente.

$ 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 ===============================

Aunque escribió solo dos funciones de prueba, pytest pudo crear ocho pruebas en total gracias a la función parametrize().

Paso 3: Migración de una prueba existente a un accesorio

  1. Agregue una nueva prueba basada en clases al archivo test_advanced.py . Esta prueba debe usar una función setup() y teardown() que crea un archivo temporal con texto sobre él. Después de cada prueba, el archivo se elimina. Debería tener este aspecto:

    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"
    

    Esta clase de prueba crea un archivo, pero es problemático porque no se garantiza que la ruta de acceso /tmp/ esté presente en todos los sistemas.

  2. Cree un accesorio de prueba que use el accesorio de prueba pytesttmpdir() para escribir en el archivo y devolver la ruta de acceso:

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

    El accesorio de prueba tmpfile() usa el accesorio de prueba tmpdir() de Pytest, que garantiza un archivo temporal válido que se limpia una vez realizadas las pruebas.

  3. Actualice la TestFile clase para que use el accesorio en lugar de los métodos auxiliares:

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

    Esta clase de prueba ahora puede garantizar que se crea y se limpia un archivo temporal con el contenido adecuado para que funcione la aserción.

Compruebe su trabajo

Por ahora debería tener un archivo de Python denominado test_advanced.py con el código siguiente:

  • Función str_to_bool() que acepta una cadena y devuelve un valor booleano en función del contenido de la cadena.
  • Dos pruebas parametrizadas para la función str_to_bool(); una que prueba los valores True y la otra que prueba los valores False.
  • Un accesorio de prueba de pytest personalizado que usa el accesorio de prueba tmpdir() para crear un archivo done temporal con contenido.
  • Clase de prueba que usa el accesorio personalizado tmpfile() para crear el archivo.

Todas las pruebas deben superarse al ejecutarlas en el terminal, sin errores.