El camino de las pruebas unitarias de Python: una guía completa desde el comienzo hasta el dominio

En este artículo, exploraremos en profundidad todos los aspectos de las pruebas unitarias de Python, incluidos sus conceptos básicos, conocimientos básicos, métodos prácticos, temas avanzados, cómo realizar pruebas unitarias en proyectos reales, las mejores prácticas para las pruebas unitarias y algunas herramientas útiles y Recursos

1. La importancia de las pruebas unitarias

Las pruebas son una parte indispensable del desarrollo de software y pueden ayudarnos a garantizar la calidad del código, reducir errores y mejorar la estabilidad del sistema. Entre varios métodos de prueba, la prueba unitaria es especialmente popular entre los desarrolladores debido a sus características rápidas y efectivas. Este artículo brindará una introducción completa a las pruebas unitarias en Python.

1.1 ¿Por qué son importantes las pruebas unitarias?

En el proceso de escribir código, podemos encontrar varios problemas, y si estos problemas no se manejan adecuadamente, a menudo se convertirán en errores imprevistos después de que el proyecto entre en línea. Estos errores no solo afectarán la experiencia del usuario, sino que también pueden causar graves pérdidas económicas. Por lo tanto, las pruebas unitarias son particularmente importantes, pueden ayudarnos a descubrir y resolver problemas en el proceso de desarrollo de código y evitar la acumulación y amplificación de problemas.

Por ejemplo, cuando escribimos una función de suma simple:

def add(x, y):
    return x + y

Podemos asegurar la funcionalidad de esta función escribiendo una prueba unitaria simple:

import unittest

class TestAdd(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(1, 2), 3)

Al ejecutar esta prueba, podemos verificar addque la función funciona correctamente.

1.2 Aplicación de pruebas unitarias en Python

Python tiene un módulo incorporado unittestque podemos usar para pruebas unitarias. Además, la comunidad de Python también proporciona algunas otras herramientas de prueba unitaria, como pytest, noseetc. Este artículo presentará principalmente cómo usar unittestlos módulos de Python para las pruebas unitarias.

En el proceso de desarrollo de Python, una buena prueba unitaria no solo puede ayudarnos a asegurar la calidad del código, sino que también puede servir como un documento para ayudar a otros desarrolladores a comprender y usar nuestro código. Por lo tanto, las pruebas unitarias ocupan una posición muy importante en el proceso de desarrollo de Python.

2. Conocimientos básicos de pruebas unitarias de Python

Antes de introducir el funcionamiento específico de las pruebas unitarias, debemos comprender algunos conocimientos básicos. En esta parte, aprenderemos qué son las pruebas unitarias y el módulo unittest de Python.

2.1 ¿Qué son las pruebas unitarias?

Las pruebas unitarias (Unit Testing) es un método de prueba de software cuyo objetivo es verificar si el comportamiento de cada unidad independiente (generalmente una función, método o clase) en el código cumple con nuestras expectativas. Las pruebas unitarias tienen muchas ventajas, como retroalimentación rápida e instantánea, problemas fáciles de localizar, etc., y son una parte importante del desarrollo basado en pruebas (TDD).

Por ejemplo, tenemos una función para elevar al cuadrado un número:

def square(n):
    return n * n

Podemos escribir una prueba unitaria para verificar que esta función funciona correctamente:

import unittest

class TestSquare(unittest.TestCase):
    def test_square(self):
        self.assertEqual(square(2), 4)
        self.assertEqual(square(-2), 4)
        self.assertEqual(square(0), 0)

De esta forma, siempre que se modifique nuestro código, podemos comprobar rápidamente si hay algún problema ejecutando esta prueba unitaria.

2.2 Introducción al módulo unittest de Python

El unittestmódulo de Python es un módulo para pruebas unitarias en la biblioteca estándar de Python, que proporciona un amplio conjunto de API para que escribamos y ejecutemos pruebas unitarias. unittestEl uso del módulo incluye principalmente tres pasos:

  1. Importar unittestel módulo.
  2. Defina una unittest.TestCaseclase de prueba que herede y luego defina varios métodos de prueba en esta clase (el nombre del método comienza con test_).
  3. Ejecute las pruebas en la línea de comandos.

Aquí hay un ejemplo simple:

import unittest

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(1 + 1, 2)

    def test_subtract(self):
        self.assertEqual(3 - 2, 1)

if __name__ == '__main__':
    unittest.main()

Ejecutar este script en la línea de comando ejecutará todos los métodos de prueba y generará los resultados de la prueba.

3. Práctica de pruebas unitarias de Python

Ahora que comprendemos los conceptos básicos de las pruebas unitarias, nos pondremos en práctica. En esta parte, demostraremos cómo escribir y ejecutar pruebas unitarias en Python.

3.1 ¿Cómo escribir una prueba de unidad básica?

En Python, podemos usar unittestmódulos para escribir pruebas unitarias. Una prueba unitaria básica generalmente consta de las siguientes partes:

  1. Importar unittestel módulo.
  2. Defina una unittest.TestCaseclase de prueba de la que se herede.
  3. En esta clase de prueba se definen varios métodos de prueba (nombres de métodos que comienzan con ) test_.
  4. En estos métodos de prueba se utilizan varios métodos de aserción unittest.TestCasepara verificar el comportamiento del código bajo prueba.

Por ejemplo, tenemos la siguiente función:

def divide(x, y):
    if y == 0:
        raise ValueError("Can not divide by zero!")
    return x / y

Podemos escribir pruebas unitarias como esta:

import unittest

class TestDivide(unittest.TestCase):
    def test_divide(self):
        self.assertEqual(divide(4, 2), 2)
        self.assertEqual(divide(-4, 2), -2)
        self.assertRaises(ValueError, divide, 4, 0)

if __name__ == '__main__':
    unittest.main()

En este ejemplo, hemos usado el unittest.TestCasemétodo assertEqualy assertRaisesel método para comprobar divideel comportamiento de la función.

3.2 Concepto y creación de casos de prueba, suites de prueba y corredores de prueba

En unittestel módulo, tenemos los siguientes conceptos importantes:

  • Caso de prueba: Un caso de prueba es un proceso de prueba completo, que incluye la preparación antes de la prueba, la ejecución de la acción de prueba y la limpieza después de la prueba. En unittestun módulo, un caso de prueba es una unittest.TestCaseinstancia de un caso de prueba.
  • Conjunto de pruebas: un conjunto de pruebas es una colección de casos de prueba o conjuntos de pruebas. Podemos usar unittest.TestSuiteclases para crear suites de prueba.
  • Corredor de pruebas: un corredor de pruebas se utiliza para ejecutar y controlar pruebas. Podemos usar unittest.TextTestRunnerclases para crear un corredor de prueba textual simple.

Aquí hay un ejemplo:

import unittest

class TestMath(unittest.TestCase):
    # 测试用例
    def test_add(self):
        self.assertEqual(1 + 1, 2)

    def test_subtract(self):
        self.assertEqual(3 - 2, 1)

# 创建测试套件
suite = unittest.TestSuite()
suite.addTest(TestMath('test_add'))
suite.addTest(TestMath('test_subtract'))

# 创建测试运行器
runner = unittest.TextTestRunner()
runner.run(suite)

En este ejemplo, creamos un conjunto de pruebas con dos casos de prueba y luego ejecutamos el conjunto de pruebas con un corredor de prueba de texto.

3.3 Use la configuración y el desmontaje para manejar la preparación y la limpieza antes y después de la prueba

Al escribir pruebas unitarias, a menudo necesitamos hacer alguna preparación y limpieza antes y después de ejecutar cada método de prueba. Por ejemplo, es posible que necesitemos crear algunos objetos antes de que comience cada método de prueba y luego destruir estos objetos después de que finalice cada método de prueba. setUpPodemos definir y métodos en la clase de prueba tearDownpara lograr estas funciones.

import unittest

class TestDatabase(unittest.TestCase):
    def setUp(self):
        # 创建数据库连接
        self.conn = create_database_connection()

    def tearDown(self):
        # 关闭数据库连接
        self.conn.close()

    def test_insert(self):
        # 使用数据库连接进行测试
        self.conn.insert(...)

En este ejemplo, setUpcreamos una conexión de base de datos en el método y tearDowncerramos la conexión de base de datos en el método. De esta forma, podemos usar esta conexión de base de datos para realizar pruebas en cada método de prueba sin crear y destruir la conexión de base de datos en cada método de prueba.

Cuarto, temas avanzados de pruebas unitarias de Python

Hemos aprendido los conceptos básicos y el uso de las pruebas unitarias de Python. Ahora, nos sumergiremos en algunos temas avanzados, incluido el desarrollo basado en pruebas (TDD), la simulación de objetos (Mocking) y las pruebas parametrizadas.

4.1 Desarrollo dirigido por pruebas (TDD)

Test-Driven Development (TDD para abreviar) es una metodología de desarrollo de software que enfatiza la escritura de pruebas unitarias antes de escribir código. Los pasos básicos de TDD son:

  1. Escriba primero una prueba unitaria fallida.
  2. Escriba código para que pase esta prueba unitaria.
  3. Refactoriza el código para hacerlo mejor.

TDD nos ayuda a mantener la calidad del código y también hace que nuestro código sea más fácil de mantener y modificar.

4.2 Burlarse

Al escribir pruebas unitarias, a veces necesitamos simular algunos factores externos e incontrolables, como el tiempo, la base de datos, las solicitudes de red, etc. Los módulos de Python unittest.mockproporcionan una forma de crear objetos simulados que podemos usar para simular factores externos e incontrolables.

Por ejemplo, supongamos que tenemos una función que decide qué devolver en función de la hora actual:

import datetime

def get_greeting():
    current_hour = datetime.datetime.now().hour
    if current_hour < 12:
        return "Good morning!"
    elif current_hour < 18:
        return "Good afternoon!"
    else:
        return "Good evening!"

Podemos unittest.mocksimular la hora actual con , para probar esta función:

import unittest
from unittest.mock import patch

class TestGreeting(unittest.TestCase):
    @patch('datetime.datetime')
    def test_get_greeting(self, mock_datetime):
        mock_datetime.now.return_value.hour = 9
        self.assertEqual(get_greeting(), "Good morning!")

        mock_datetime.now.return_value.hour = 15
        self.assertEqual(get_greeting(), "Good afternoon!")

        mock_datetime.now.return_value.hour = 20
        self.assertEqual(get_greeting(), "Good evening!")

if __name__ == '__main__':
    unittest.main()

En este ejemplo, usamos unittest.mock.patchpara simular datetime.datetimeel objeto y luego establecemos nowel valor de retorno de su método.

4.3 Pruebas paramétricas

La prueba parametrizada es una técnica de prueba unitaria que nos permite ejecutar la misma prueba con diferentes datos de entrada. En unittestlos módulos de Python, podemos usar unittest.subTestadministradores de contexto para implementar pruebas parametrizadas.

Aquí hay un ejemplo:

import unittest

class TestSquare(unittest.TestCase):
    def test_square(self):
        for i in range(-10, 11):
            with self.subTest(i=i):
                self.assertEqual(square(i), i * i)

if __name__ == '__main__':
    unittest.main()

En este ejemplo, usamos unittest.subTestel administrador de contexto para ejecutar 20 pruebas diferentes, cada una con datos de entrada diferentes.

5. Ejercicio práctico: ejemplo de proyecto completo para pruebas unitarias de Python

En esta parte, veremos un proyecto simple para mostrar cómo aplicar las pruebas unitarias de Python en la práctica. Crearemos una sencilla aplicación de calculadora de fracciones que pueda sumar, restar, multiplicar y dividir fracciones.

5.1 Crear un proyecto

Primero, creamos un nuevo proyecto de Python y creamos un fraction_calculator.pyarchivo dentro del proyecto. En este archivo, definimos una Fractionclase para representar puntuaciones. Esta clase tiene dos propiedades: numerador y denominador.

# fraction_calculator.py

class Fraction:
    def __init__(self, numerator, denominator):
        if denominator == 0:
            raise ValueError("Denominator cannot be zero!")
        self.numerator = numerator
        self.denominator = denominator

5.2 Escribir pruebas unitarias

Luego, creamos un test_fraction_calculator.pyarchivo donde escribimos pruebas unitarias para probar la Fractionclase.

# test_fraction_calculator.py

import unittest
from fraction_calculator import Fraction

class TestFraction(unittest.TestCase):
    def test_create_fraction(self):
        f = Fraction(1, 2)
        self.assertEqual(f.numerator, 1)
        self.assertEqual(f.denominator, 2)

    def test_create_fraction_with_zero_denominator(self):
        with self.assertRaises(ValueError):
            Fraction(1, 0)

if __name__ == '__main__':
    unittest.main()

En esta clase de prueba, creamos dos métodos de prueba: test_create_fractionpara probar que las fracciones se crean normalmente y test_create_fraction_with_zero_denominatorpara probar que se debe lanzar una excepción cuando el denominador es cero.

5.3 Unidades ejecutoras de pruebas

Finalmente, ejecutamos el archivo en la línea de comando test_fraction_calculator.pypara ejecutar las pruebas unitarias.

python -m unittest test_fraction_calculator.py

Si pasan todas las pruebas, entonces podemos decir con confianza que nuestra Fractionclase es correcta.

5.4 Elementos ampliados

Por supuesto, nuestro proyecto está lejos de estar completo. FractionLa clase también necesita agregar muchas funciones, como operaciones de suma, resta, multiplicación y división, reducción de fracciones, conversión a números de coma flotante, etc. Para cada nueva función, necesitamos escribir las pruebas unitarias correspondientes para garantizar su corrección. Y también necesitamos ejecutar estas pruebas unitarias constantemente para asegurarnos de que nuestras modificaciones no rompan la funcionalidad existente.

Las pruebas unitarias son un proceso continuo, no una tarea única. Solo escribiendo y ejecutando constantemente pruebas unitarias podemos garantizar la calidad y confiabilidad de nuestro código.

Six, la mejor práctica de las pruebas unitarias de Python

En el proceso de escribir y ejecutar pruebas unitarias de Python, existen algunas mejores prácticas que pueden ayudarnos a mejorar la eficiencia de nuestro trabajo y garantizar la calidad y confiabilidad de nuestras pruebas.

6.1 Siempre escriba las pruebas primero

De acuerdo con el principio del desarrollo basado en pruebas (TDD), primero debemos escribir la prueba y luego escribir el código que puede pasar la prueba. Esto puede ayudarnos a comprender las funciones que queremos lograr con mayor claridad y también garantizar que nuestro código sea comprobable.

6.2 Mantenga las pruebas independientes

Cada prueba debe ser independiente y no depender de otras pruebas. Si hay dependencias entre las pruebas, la falla de una prueba puede causar que otras pruebas también fallen, lo que hace que los resultados de la prueba sean difíciles de entender y que las pruebas sean más difíciles de mantener.

6.3 Probar todos los casos posibles

Deberíamos intentar probar todos los casos posibles, incluidos los casos normales, los casos extremos y los casos anormales. Por ejemplo, si tenemos una función que toma un número entero entre 0 y 100 como argumento, entonces deberíamos probar cómo se comporta esta función con argumentos de 0, 50, 100 y otros valores.

6.4 Uso de objetos ficticios

Al probar código que involucra sistemas externos (como bases de datos, servicios de red, etc.), podemos usar objetos simulados (Mocking) en lugar de sistemas externos reales. Esto hace que las pruebas sean más rápidas, más estables y más controlables.

6.5 Realizar pruebas con regularidad

Deberíamos ejecutar nuestras pruebas regularmente para asegurarnos de que nuestro código no esté roto. Una práctica común es ejecutar pruebas antes de cada confirmación de código. Además, también podemos utilizar herramientas de integración continua (Continuous Integration), como Jenkins, Travis CI, etc., para ejecutar automáticamente nuestras pruebas.

6.6 Uso de herramientas de cobertura de código

La cobertura de código es una métrica utilizada para indicar cuánto código cubren nuestras pruebas. Podemos usar herramientas de cobertura de código, como la cobertura.py, para medir nuestra cobertura de código y trabajar duro para mejorar esta métrica. Sin embargo, tenga en cuenta que la cobertura del código no garantiza la calidad ni la integridad de nuestras pruebas. Es solo una herramienta, no podemos confiar demasiado en ella.

# 运行代码覆盖率工具的示例
# 在命令行中输入以下命令:

$ coverage run --source=. -m unittest discover
$ coverage report

El comando anterior primero ejecutará todas las pruebas unitarias y recopilará información de cobertura de código. Luego mostrará un informe de cobertura de código que le indicará qué código está cubierto por las pruebas y cuál no.

7. Herramientas y recursos

Existen algunas herramientas y recursos que pueden ayudarnos a mejorar la eficiencia y la calidad al realizar pruebas unitarias de Python.

7.1 Módulo de prueba de unidad incorporado de Python

El módulo unittest incorporado de Python es un poderoso marco de pruebas unitarias que proporciona métodos de aserción enriquecidos, conjuntos de pruebas y ejecutores de pruebas. Si desea realizar pruebas unitarias, el módulo unittest es un excelente lugar para comenzar.

# unittest模块的基本使用
import unittest

class TestMyFunction(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(1, 2), 3)

if __name__ == '__main__':
    unittest.main()

7.2 pytest

pytest es un marco de prueba de Python popular que es más compacto y poderoso que unittest. Puede usarse no solo para pruebas unitarias, sino también para pruebas funcionales, pruebas de integración, etc.

# pytest的基本使用
def test_add():
    assert add(1, 2) == 3

7.3 simulacro

El módulo simulado lo ayuda a crear objetos simulados para sustituir objetos reales en las pruebas. Esto es útil para probar código que depende de sistemas u objetos externos que son difíciles de construir.

# mock模块的基本使用
from unittest.mock import Mock

# 创建一个模拟对象
mock = Mock()
# 设置模拟对象的返回值
mock.return_value = 42
# 使用模拟对象
assert mock() == 42

7.4 cobertura.py

covery.py es una herramienta de cobertura de código que lo ayuda a descubrir qué código no está cubierto por las pruebas.

# coverage.py的基本使用
coverage run --source=. -m unittest discover
coverage report

7.5 Pruebas de Python

Python Testing es un sitio web sobre pruebas de Python que proporciona muchos tutoriales, herramientas, libros y otros recursos sobre pruebas de Python. La URL es: http://pythontesting.net

8. Resumen

Espero que a través de este artículo, tenga una comprensión y una aplicación más profundas de las pruebas unitarias de Python. Las pruebas unitarias son una parte muy importante del proceso de desarrollo de software. Las pruebas unitarias correctas pueden ayudarnos a mejorar la calidad del código, encontrar y solucionar problemas y mejorar la eficiencia del desarrollo. Python proporciona una serie de herramientas poderosas para las pruebas unitarias, y estas herramientas pueden ayudarnos a escribir mejores pruebas unitarias.

En el proceso de escribir pruebas unitarias, no solo podemos encontrar y solucionar problemas, sino también comprender profundamente nuestro código y lógica comercial, y mejorar nuestras habilidades de programación.

Si es útil, preste más atención a la cuenta pública personal de WeChat: [Perspectiva completa de Python] TeahLead_KrisChang, más de 10 años de experiencia en la industria de Internet e inteligencia artificial, más de 10 años de experiencia en tecnología y gestión de equipos comerciales, Tongji Software Licenciado en Ingeniería, Máster en Gestión de Ingeniería de Fudan, Arquitecto sénior de servicios en la nube certificado por Aliyun, Jefe de negocio de productos de IA con cientos de millones de ingresos.

Supongo que te gusta

Origin blog.csdn.net/magicyangjay111/article/details/131783547
Recomendado
Clasificación