Introducci贸n
Contenido
En casi todos los campos, los productos se prueban minuciosamente antes de ser lanzados al mercado para garantizar su calidad y que funcionan seg煤n lo previsto.
Los medicamentos, los productos cosm茅ticos, los veh铆culos, los tel茅fonos y las computadoras port谩tiles se prueban para garantizar que mantengan un cierto nivel de calidad que se prometi贸 al consumidor. Dada la influencia y el alcance del software en nuestra vida diaria, es importante que probemos nuestro software minuciosamente antes de lanzarlo a nuestros usuarios para evitar que surjan problemas cuando est茅 en uso.
Hay varias formas y m茅todos de probar nuestro software, y en este art铆culo nos concentraremos en probar nuestros programas Python usando el Prueba de unidad marco de referencia.
Prueba unitaria frente a otras formas de prueba
Hay varias formas de probar software que se agrupan principalmente en pruebas funcionales y no funcionales.
- Pruebas no funcionales: Destinado a verificar y comprobar los aspectos no funcionales del software, como la fiabilidad, la seguridad, la disponibilidad y la escalabilidad. Ejemplos de pruebas no funcionales incluyen pruebas de carga y pruebas de estr茅s.
- Pruebas funcionales: Implica probar nuestro software con los requisitos funcionales para garantizar que ofrece la funcionalidad requerida. Por ejemplo, podemos probar si nuestra plataforma de compras env铆a correos electr贸nicos a los usuarios despu茅s de realizar sus pedidos simulando ese escenario y verificando el correo electr贸nico.
Las pruebas unitarias se incluyen en las pruebas funcionales junto con las pruebas de integraci贸n y las pruebas de regresi贸n.
Las pruebas unitarias se refieren a un m茅todo de prueba en el que el software se divide en diferentes componentes (unidades) y cada unidad se prueba funcionalmente y de forma aislada de las otras unidades o m贸dulos.
Aqu铆, una unidad se refiere a la parte m谩s peque帽a de un sistema que logra una 煤nica funci贸n y se puede probar. El objetivo de las pruebas unitarias es verificar que cada componente de un sistema funcione seg煤n lo esperado, lo que a su vez confirma que todo el sistema cumple y cumple los requisitos funcionales.
Las pruebas unitarias generalmente se realizan antes de las pruebas de integraci贸n, ya que, para verificar que las partes de un sistema funcionan bien juntas, primero debemos verificar que funcionen como se espera individualmente. Generalmente tambi茅n lo llevan a cabo los desarrolladores que construyen los componentes individuales durante el proceso de desarrollo.
Beneficios de las pruebas unitarias
Las pruebas unitarias son beneficiosas porque corrigen errores y problemas al principio del proceso de desarrollo y, finalmente, lo acelera.
El costo de corregir los errores identificados durante las pruebas unitarias tambi茅n es bajo en comparaci贸n con corregirlos durante las pruebas de integraci贸n o durante la producci贸n.
Las pruebas unitarias tambi茅n sirven como documentaci贸n del proyecto al definir lo que hace cada parte del sistema a trav茅s de pruebas bien escritas y documentadas. Al refactorizar un sistema o agregar funciones, las pruebas unitarias ayudan a protegerse contra cambios que rompen la funcionalidad existente.
Marco de prueba unitaria
Inspirado en el marco de pruebas JUnit para Java, unittest
es un marco de prueba para programas de Python que viene incluido con la distribuci贸n de Python desde Python 2.1. A veces se lo denomina PyUnit. El marco admite la automatizaci贸n y agregaci贸n de pruebas y el c贸digo com煤n de configuraci贸n y apagado para ellas.
Lo logra y m谩s a trav茅s de los siguientes conceptos:
- Accesorio de prueba: Define la preparaci贸n necesaria para la ejecuci贸n de las pruebas y las acciones que deben realizarse despu茅s de la conclusi贸n de una prueba. Los accesorios pueden incluir la configuraci贸n y conexi贸n de la base de datos, la creaci贸n de archivos o directorios temporales y la limpieza o eliminaci贸n posterior de los archivos despu茅s de que se haya completado la prueba.
- Caso de prueba: Se refiere a la prueba individual que busca una respuesta espec铆fica en un escenario dado con entradas espec铆ficas.
- Banco de pruebas: Representa una agregaci贸n de casos de prueba que est谩n relacionados y deben ejecutarse juntos.
- Corredor de pruebas: Coordina la ejecuci贸n de las pruebas y proporciona los resultados del proceso de prueba al usuario a trav茅s de una interfaz gr谩fica de usuario, el terminal o un informe escrito en un archivo.
unittest
no es el 煤nico marco de prueba para Python que existe, otros incluyen Pytest, Marco de robot, Lechuga para BDD, y Behave Framework.
Si est谩 interesado en leer m谩s sobre el desarrollo basado en pruebas en Python con PyTest, 隆lo tenemos cubierto!
Marco Unittest en acci贸n
Vamos a explorar el unittest
framework construyendo una aplicaci贸n de calculadora simple y escribiendo las pruebas para verificar que funciona como se esperaba. Usaremos el proceso de desarrollo basado en pruebas comenzando con las pruebas y luego implementando la funcionalidad para que las pruebas pasen.
Si bien es una buena pr谩ctica desarrollar nuestra aplicaci贸n Python en un entorno virtual, para este ejemplo no ser谩 obligatorio ya que unittest
se env铆a con la distribuci贸n de Python y no necesitaremos ning煤n otro paquete externo para construir nuestra calculadora.
Nuestra calculadora realizar谩 operaciones simples de suma, resta, multiplicaci贸n y divisi贸n entre dos n煤meros enteros. Estos requisitos guiar谩n nuestras pruebas funcionales utilizando el unittest
marco de referencia.
Probaremos las cuatro operaciones admitidas por nuestra calculadora por separado y escribiremos las pruebas para cada una en un conjunto de pruebas separado, ya que se espera que las pruebas de una operaci贸n en particular se ejecuten juntas. Nuestras suites de prueba se alojar谩n en un archivo y nuestra calculadora en un archivo separado.
Nuestra calculadora ser谩 un SimpleCalculator
clase con funciones para manejar las cuatro operaciones que se esperan de ella. Comencemos a probar escribiendo las pruebas para la operaci贸n de suma en nuestro test_simple_calculator.py
:
import unittest
from simple_calculator import SimpleCalculator
class AdditionTestSuite(unittest.TestCase):
def setUp(self):
""" Executed before every test case """
self.calculator = SimpleCalculator()
def tearDown(self):
""" Executed after every test case """
print("ntearDown executing after the test case. Result:")
def test_addition_two_integers(self):
result = self.calculator.sum(5, 6)
self.assertEqual(result, 11)
def test_addition_integer_string(self):
result = self.calculator.sum(5, "6")
self.assertEqual(result, "ERROR")
def test_addition_negative_integers(self):
result = self.calculator.sum(-5, -6)
self.assertEqual(result, -11)
self.assertNotEqual(result, 11)
# Execute all the tests when the file is executed
if __name__ == "__main__":
unittest.main()
Empezamos importando el unittest
m贸dulo y creando una suite de pruebas (AdditionTestSuite
) para la operaci贸n de adici贸n.
En 茅l, creamos un setUp()
m茅todo que se llama antes de cada caso de prueba para crear nuestro SimpleCalculator
objeto que se utilizar谩 para realizar los c谩lculos.
los tearDown()
El m茅todo se ejecuta despu茅s de cada caso de prueba y, dado que no lo utilizamos mucho en este momento, solo lo usaremos para imprimir los resultados de cada prueba.
Las funciones test_addition_two_integers()
, test_addition_integer_string()
y test_addition_negative_integers()
son nuestros casos de prueba. Se espera que la calculadora sume dos n煤meros enteros positivos o negativos y devuelva la suma. Cuando se le presenta un n煤mero entero y una cadena, se supone que nuestra calculadora devuelve un error.
los assertEqual()
y assertNotEqual()
son funciones que se utilizan para validar la salida de nuestra calculadora. los assertEqual()
La funci贸n comprueba si los dos valores proporcionados son iguales, en nuestro caso, esperamos la suma de 5
y 6
ser – estar 11
, por lo que compararemos esto con el valor devuelto por nuestra calculadora.
Si los dos valores son iguales, la prueba ha pasado. Otras funciones de aserci贸n ofrecidas por unittest
incluir:
assertTrue(a)
: Comprueba si la expresi贸n proporcionada estrue
assertGreater(a, b)
: Comprueba sia
es mayor queb
assertNotIn(a, b)
: Comprueba sia
es enb
assertLessEqual(a, b)
: Comprueba sia
es menor o igual ab
- etc …
Puede encontrar una lista de estas afirmaciones en esta hoja de trucos.
Cuando ejecutamos el archivo de prueba, esta es la salida:
$ python3 test_simple_calulator.py
tearDown executing after the test case. Result:
E
tearDown executing after the test case. Result:
E
tearDown executing after the test case. Result:
E
======================================================================
ERROR: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_simple_calulator.py", line 22, in test_addition_integer_string
result = self.calculator.sum(5, "6")
AttributeError: 'SimpleCalculator' object has no attribute 'sum'
======================================================================
ERROR: test_addition_negative_integers (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_simple_calulator.py", line 26, in test_addition_negative_integers
result = self.calculator.sum(-5, -6)
AttributeError: 'SimpleCalculator' object has no attribute 'sum'
======================================================================
ERROR: test_addition_two_integers (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_simple_calulator.py", line 18, in test_addition_two_integers
result = self.calculator.sum(5, 6)
AttributeError: 'SimpleCalculator' object has no attribute 'sum'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (errors=3)
En la parte superior de la salida, podemos ver la ejecuci贸n del tearDown()
funci贸n mediante la impresi贸n del mensaje que especificamos. A esto le sigue la letra E
y mensajes de error derivados de la ejecuci贸n de nuestras pruebas.
Hay tres posibles resultados de una prueba, puede pasar, fallar o encontrar un error. los unittest
marco indica los tres escenarios mediante:
- Un punto final (
.
): Indica una prueba aprobada - La letra ‘F’: Indica una prueba fallida
- La letra ‘E’: Indica que ocurri贸 un error durante la ejecuci贸n de la prueba.
En nuestro caso, estamos viendo la letra E
, lo que significa que nuestras pruebas encontraron errores que ocurrieron al ejecutar nuestras pruebas. Recibimos errores porque a煤n no hemos implementado el addition
funcionalidad de nuestra calculadora:
class SimpleCalculator:
def sum(self, a, b):
""" Function to add two integers """
return a + b
Nuestra calculadora ahora est谩 lista para sumar dos n煤meros, pero para estar seguros de que funcionar谩 como se espera, eliminemos el tearDown()
funcionar de nuestras pruebas y ejecutar nuestras pruebas una vez m谩s:
$ python3 test_simple_calulator.py
E..
======================================================================
ERROR: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_simple_calulator.py", line 22, in test_addition_integer_string
result = self.calculator.sum(5, "6")
File "/Users/robley/Desktop/code/python/unittest_demo/src/simple_calculator.py", line 7, in sum
return a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (errors=1)
Nuestros errores se han reducido de 3 a solo una vez 1. El resumen del informe en la primera l铆nea E..
indica que una prueba result贸 en un error y no pudo completar la ejecuci贸n, y las dos restantes pasaron. Para realizar la primera prueba, tenemos que refactorizar nuestra funci贸n de suma de la siguiente manera:
def sum(self, a, b):
if isinstance(a, int) and isinstance(b, int):
return a + b
Cuando ejecutamos nuestras pruebas una vez m谩s:
$ python3 test_simple_calulator.py
F..
======================================================================
FAIL: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_simple_calulator.py", line 23, in test_addition_integer_string
self.assertEqual(result, "ERROR")
AssertionError: None != 'ERROR'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
Esta vez, nuestra funci贸n de suma se ejecuta hasta el final, pero nuestra prueba falla. Esto se debe a que no devolvimos ning煤n valor cuando una de las entradas no es un n煤mero entero. Nuestra afirmaci贸n se compara None
a ERROR
y como no son iguales, la prueba falla. Para hacer que nuestra prueba pase tenemos que devolver el error en nuestro sum()
funci贸n:
def sum(self, a, b):
if isinstance(a, int) and isinstance(b, int):
return a + b
else:
return "ERROR"
Y cuando ejecutamos nuestras pruebas:
$ python3 test_simple_calulator.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Todas nuestras pruebas pasan ahora y obtenemos 3 paradas completas para indicar que todas nuestras 3 pruebas para la funcionalidad de adici贸n est谩n pasando. Los conjuntos de pruebas de resta, multiplicaci贸n y divisi贸n tambi茅n se implementan de manera similar.
Tambi茅n podemos probar si se genera una excepci贸n. Por ejemplo, cuando un n煤mero se divide por cero, el ZeroDivisionError
se plantea una excepci贸n. En nuestro DivisionTestSuite
, podemos confirmar si se gener贸 la excepci贸n:
class DivisionTestSuite(unittest.TestCase):
def setUp(self):
""" Executed before every test case """
self.calculator = SimpleCalculator()
def test_divide_by_zero_exception(self):
with self.assertRaises(ZeroDivisionError):
self.calculator.divide(10, 0)
los test_divide_by_zero_exception()
ejecutar谩 el divide(10, 0)
funci贸n de nuestra calculadora y confirmar que la excepci贸n efectivamente se plante贸. Podemos ejecutar el DivisionTestSuite
de forma aislada, como sigue:
$ python3 -m unittest test_simple_calulator.DivisionTestSuite.test_divide_by_zero_exception
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
El conjunto de pruebas de funcionalidad de divisi贸n completa se puede encontrar en la esencia vinculada a continuaci贸n junto con los conjuntos de pruebas para la funcionalidad de multiplicaci贸n y resta.
Conclusi贸n
En este art铆culo, hemos explorado el unittest
framework e identific贸 las situaciones en las que se utiliza al desarrollar programas Python. los unittest
framework, tambi茅n conocido como PyUnit, viene con la distribuci贸n Python por defecto a diferencia de otros frameworks de prueba. De manera TDD, escribimos las pruebas para una calculadora simple, ejecutamos las pruebas y luego implementamos la funcionalidad para que las pruebas pasaran.
los unittest
framework proporcion贸 la funcionalidad para crear y agrupar casos de prueba y verificar la salida de nuestra calculadora con la salida esperada para verificar que est茅 funcionando como se esperaba.
La calculadora completa y los conjuntos de pruebas se pueden encontrar aqu铆 en聽GitHub.