Elementos esenciales de Pruebas en Python

Beginner

This tutorial is from open-source community. Access the source code

Introducción

La naturaleza dinámica de Python hace que las pruebas sean críticamente importantes para la mayoría de las aplicaciones. No hay un compilador para encontrar tus errores. La única manera de encontrar errores es ejecutar el código y asegurarse de probar todas sus funciones.

Aserciones

La declaración assert es una comprobación interna para el programa. Si una expresión no es verdadera, lanza una excepción AssertionError.

Sintaxis de la declaración assert.

assert <expresión> [, 'Mensaje de diagnóstico']

Por ejemplo.

assert isinstance(10, int), 'Se esperaba un int'

No debe usarse para comprobar la entrada del usuario (es decir, los datos ingresados en un formulario web o algo por el estilo). Su propósito es más para comprobaciones internas e invariantes (condiciones que siempre deben ser verdaderas).

Programación por Contrato

También conocida como Diseño por Contrato, el uso abundante de aserciones es un enfoque para el diseño de software. Establece que los diseñadores de software deben definir especificaciones de interfaz precisas para los componentes del software.

Por ejemplo, es posible que coloques aserciones en todas las entradas de una función.

def add(x, y):
    assert isinstance(x, int), 'Expected int'
    assert isinstance(y, int), 'Expected int'
    return x + y

Comprobar las entradas capturará inmediatamente a los llamantes que no están usando argumentos adecuados.

>>> add(2, 3)
5
>>> add('2', '3')
Traceback (most recent call last):
...
AssertionError: Expected int
>>>

Pruebas en línea

Las aserciones también se pueden utilizar para pruebas simples.

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

assert add(2,2) == 4

De esta manera, se incluye la prueba en el mismo módulo que el código.

Ventaja: Si el código está obviamente roto, las intentos de importar el módulo se detendrán con un error.

No se recomienda utilizar esto para pruebas exhaustivas. Es más una "prueba de humo" básica. ¿Funciona la función con cualquier ejemplo? Si no, entonces algo está definitivamente roto.

Módulo unittest

Supongamos que tienes un código en simple.py.

## simple.py

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

Ahora, supongamos que quieres probarlo. Crea un archivo de prueba separado como este en /home/labex/project/test_simple.py.

## test_simple.py

import simple
import unittest

Luego, define una clase de prueba.

## test_simple.py

import simple
import unittest

## Observa que hereda de unittest.TestCase
class TestAdd(unittest.TestCase):
 ...

La clase de prueba debe heredar de unittest.TestCase.

En la clase de prueba, defines los métodos de prueba.

## test_simple.py

import simple
import unittest

## Observa que hereda de unittest.TestCase
class TestAdd(unittest.TestCase):
    def test_simple(self):
        ## Prueba con argumentos enteros simples
        r = simple.add(2, 2)
        self.assertEqual(r, 5)
    def test_str(self):
        ## Prueba con cadenas
        r = simple.add('hello', 'world')
        self.assertEqual(r, 'helloworld')

*Importante: Cada método debe comenzar con test.

Usando unittest

Hay varias aserciones incorporadas que vienen con unittest. Cada una de ellas afirma algo diferente.

## Asegura que expr sea True
self.assertTrue(expr)

## Asegura que x == y
self.assertEqual(x,y)

## Asegura que x!= y
self.assertNotEqual(x,y)

## Asegura que x sea cercano a y
self.assertAlmostEqual(x,y,places)

## Asegura que callable(arg1,arg2,...) lance exc
self.assertRaises(exc, callable, arg1, arg2,...)

Esta no es una lista exhaustiva. Hay otras aserciones en el módulo.

Ejecutando unittest

Para ejecutar las pruebas, convierte el código en un script.

## test_simple.py

...

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

Luego ejecuta Python en el archivo de prueba.

$ python3 test_simple.py
F.
========================================================
FAIL: test_simple (__main__.TestAdd)
--------------------------------------------------------
Traceback (most recent call last):
  File "testsimple.py", line 8, in test_simple
    self.assertEqual(r, 5)
AssertionError: 4!= 5
--------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (fallos=1)

Comentario

La prueba unitaria efectiva es un arte y puede volverse bastante complicada para aplicaciones grandes.

El módulo unittest tiene un gran número de opciones relacionadas con ejecutores de pruebas, recopilación de resultados y otros aspectos de las pruebas. Consulte la documentación para obtener detalles.

Herramientas de Prueba de Terceros

El módulo unittest integrado tiene la ventaja de estar disponible en todas partes: es parte de Python. Sin embargo, muchos programadores también lo encuentran bastante verboso. Una alternativa popular es pytest. Con pytest, tu archivo de prueba se simplifica a algo como lo siguiente:

## test_simple.py
import simple

def test_simple():
    assert simple.add(2,2) == 4

def test_str():
    assert simple.add('hello','world') == 'helloworld'

Para ejecutar una prueba, simplemente escribe un comando como python -m pytest. Luego buscará y ejecutará todas las pruebas. El módulo se puede instalar usando pip install pytest.

Hay mucho más en pytest que este ejemplo, pero por lo general es bastante fácil de empezar si decides probarlo.

En este ejercicio, explorarás la mecánica básica de uso del módulo unittest de Python.

En ejercicios anteriores, escribiste un archivo stock.py que contenía una clase Stock. Para este ejercicio, se asumió que estás usando el código escrito para el Ejercicio 7.9 que involucra propiedades tipadas. Si, por alguna razón, eso no está funcionando, es posible que desees copiar la solución de Soluciones/7_9 a tu directorio de trabajo.

Ejercicio 8.1: Escribir Pruebas Unitarias

En un archivo separado test_stock.py, escribe un conjunto de pruebas unitarias para la clase Stock. Para ayudarte a empezar, aquí hay un pequeño fragmento de código que prueba la creación de instancias:

## test_stock.py

import unittest
import stock

class TestStock(unittest.TestCase):
    def test_create(self):
        s = stock.Stock('GOOG', 100, 490.1)
        self.assertEqual(s.name, 'GOOG')
        self.assertEqual(s.shares, 100)
        self.assertEqual(s.price, 490.1)

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

Ejecuta tus pruebas unitarias. Deberías obtener una salida que se vea así:

.

Ran 1 tests in 0.000s

OK

Una vez que estés satisfecho de que funciona, escribe pruebas unitarias adicionales que comprueben lo siguiente:

  • Asegúrate de que la propiedad s.cost devuelva el valor correcto (49010.0)
  • Asegúrate de que el método s.sell() funcione correctamente. Debería decrementar el valor de s.shares en consecuencia.
  • Asegúrate de que el atributo s.shares no se pueda establecer en un valor no entero.

Para la última parte, tendrás que comprobar que se lance una excepción. Una forma fácil de hacer eso es con código como este:

class TestStock(unittest.TestCase):
  ...
    def test_bad_shares(self):
         s = stock.Stock('GOOG', 100, 490.1)
         with self.assertRaises(TypeError):
             s.shares = '100'

Resumen

¡Felicitaciones! Has completado el laboratorio de Pruebas. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.