Prueba de aplicaciones de subprocesos

En este capítulo, aprenderemos sobre la prueba de aplicaciones de subprocesos. También aprenderemos la importancia de las pruebas.

¿Por qué probar?

Antes de sumergirnos en la discusión sobre la importancia de las pruebas, necesitamos saber qué son las pruebas. En términos generales, las pruebas son una técnica para averiguar qué tan bien está funcionando algo. Por otro lado, específicamente si hablamos de programas de computadora o software, entonces probar es la técnica de acceder a la funcionalidad de un programa de software.

En esta sección, discutiremos la importancia de las pruebas de software. En el desarrollo de software, debe haber una doble verificación antes de entregar el software al cliente. Por eso es muy importante probar el software por un equipo de pruebas experimentado. Considere los siguientes puntos para comprender la importancia de las pruebas de software:

Mejora de la calidad del software

Ciertamente, ninguna empresa quiere ofrecer software de baja calidad y ningún cliente quiere comprar software de baja calidad. Las pruebas mejoran la calidad del software al encontrar y corregir los errores que contiene.

Satisfacción de los clientes

La parte más importante de cualquier negocio es la satisfacción de sus clientes. Al proporcionar software libre de errores y de buena calidad, las empresas pueden lograr la satisfacción del cliente.

Reducir el impacto de las nuevas funciones

Supongamos que hemos creado un sistema de software de 10000 líneas y necesitamos agregar una nueva característica, entonces el equipo de desarrollo estaría preocupado por el impacto de esta nueva característica en todo el software. Aquí, también, las pruebas juegan un papel vital porque si el equipo de pruebas ha realizado un buen conjunto de pruebas, puede salvarnos de posibles rupturas catastróficas.

Experiencia de usuario

Otra parte más importante de cualquier negocio es la experiencia de los usuarios de ese producto. Solo las pruebas pueden garantizar que el usuario final encuentre simple y fácil de usar el producto.

Reduciendo los gastos

Las pruebas pueden reducir el costo total del software al encontrar y corregir los errores en la fase de prueba de su desarrollo en lugar de corregirlos después de la entrega. Si hay un error importante después de la entrega del software, aumentaría su costo tangible, digamos en términos de gastos y costos intangibles, digamos en términos de insatisfacción del cliente, reputación negativa de la empresa, etc.

¿Qué probar?

Siempre se recomienda tener un conocimiento adecuado de lo que se va a probar. En esta sección, primero entenderemos cuál es el motivo principal del probador al probar cualquier software. Debe evitarse la cobertura de código, es decir, cuántas líneas de código llega a nuestro conjunto de pruebas, durante la prueba. Esto se debe a que, durante las pruebas, centrarse solo en la cantidad de líneas de códigos no agrega valor real a nuestro sistema. Es posible que queden algunos errores, que se reflejan más adelante en una etapa posterior incluso después de la implementación.

Considere los siguientes puntos importantes relacionados con qué probar:

  • Necesitamos enfocarnos en probar la funcionalidad del código en lugar de la cobertura del código.

  • Necesitamos probar las partes más importantes del código primero y luego avanzar hacia las partes menos importantes del código. Definitivamente ahorrará tiempo.

  • El probador debe tener una multitud de pruebas diferentes que puedan llevar el software al límite.

Enfoques para probar programas de software concurrentes

Debido a la capacidad de utilizar la verdadera capacidad de la arquitectura de múltiples núcleos, los sistemas de software concurrentes están reemplazando a los sistemas secuenciales. En los últimos tiempos, los programas de sistema concurrentes se están utilizando en todo, desde teléfonos móviles hasta lavadoras, desde automóviles hasta aviones, etc. Debemos tener más cuidado al probar los programas de software concurrentes porque si hemos agregado varios subprocesos a la aplicación de un solo ya es un error, entonces terminaríamos con varios errores.

Las técnicas de prueba para programas de software concurrentes se centran ampliamente en seleccionar el entrelazado que expone patrones potencialmente dañinos como condiciones de carrera, interbloqueos y violación de la atomicidad. A continuación se presentan dos enfoques para probar programas de software concurrentes:

Exploración sistemática

Este enfoque tiene como objetivo explorar el espacio de las intercalaciones de la manera más amplia posible. Tales enfoques pueden adoptar una técnica de fuerza bruta y otros adoptan una técnica de reducción de orden parcial o una técnica heurística para explorar el espacio de entrelazamientos.

Impulsado por la propiedad

Los enfoques basados ​​en propiedades se basan en la observación de que es más probable que ocurran fallas de simultaneidad en intercalaciones que exponen propiedades específicas, como un patrón de acceso a la memoria sospechoso. Los diferentes enfoques basados ​​en propiedades apuntan a diferentes fallas como las condiciones de carrera, los puntos muertos y la violación de la atomicidad, que depende además de una u otras propiedades específicas.

Estrategias de prueba

La estrategia de prueba también se conoce como enfoque de prueba. La estrategia define cómo se realizarían las pruebas. El enfoque de prueba tiene dos técnicas:

Proactivo

Un enfoque en el que el proceso de diseño de prueba se inicia lo antes posible para encontrar y corregir los defectos antes de que se cree la compilación.

Reactivo

Un enfoque en el que las pruebas no comienzan hasta que se completa el proceso de desarrollo.

Antes de aplicar cualquier estrategia o enfoque de prueba en un programa Python, debemos tener una idea básica sobre el tipo de errores que puede tener un programa de software. Los errores son los siguientes:

Errores sintácticos

Durante el desarrollo del programa, puede haber muchos errores pequeños. Los errores se deben principalmente a errores tipográficos. Por ejemplo, dos puntos faltantes o la ortografía incorrecta de una palabra clave, etc. Estos errores se deben a errores en la sintaxis del programa y no en la lógica. Por tanto, estos errores se denominan errores sintácticos.

Errores semánticos

Los errores semánticos también se denominan errores lógicos. Si hay un error lógico o semántico en el programa de software, la declaración se compilará y se ejecutará correctamente, pero no dará el resultado deseado porque la lógica no es correcta.

Examen de la unidad

Esta es una de las estrategias de prueba más utilizadas para probar programas de Python. Esta estrategia se utiliza para probar unidades o componentes del código. Por unidades o componentes, nos referimos a clases o funciones del código. La prueba unitaria simplifica la prueba de grandes sistemas de programación al probar unidades “pequeñas”. Con la ayuda del concepto anterior, las pruebas unitarias pueden definirse como un método en el que se prueban unidades individuales de código fuente para determinar si devuelven el resultado deseado.

En las secciones siguientes, aprenderemos sobre los diferentes módulos de Python para pruebas unitarias.

módulo unittest

El primer módulo para pruebas unitarias es el módulo unittest. Está inspirado en JUnit y por defecto se incluye en Python3.6. Admite la automatización de pruebas, el intercambio de códigos de configuración y apagado para pruebas, la agregación de pruebas en colecciones y la independencia de las pruebas del marco de informes.

A continuación se presentan algunos conceptos importantes respaldados por el módulo unittest

Accesorio de texto

Se utiliza para configurar una prueba de modo que pueda ejecutarse antes de comenzar la prueba y desmontarse una vez finalizada la prueba. Puede implicar la creación de una base de datos temporal, directorios, etc. necesarios antes de comenzar la prueba.

Caso de prueba

El caso de prueba verifica si una respuesta requerida proviene del conjunto específico de entradas o no. El módulo unittest incluye una clase base llamada TestCase que se puede utilizar para crear nuevos casos de prueba. Incluye dos métodos predeterminados:

  • setUp()- un método de gancho para configurar el dispositivo de prueba antes de ejercitarlo. Esto se llama antes de llamar a los métodos de prueba implementados.

  • tearDown( - un método de gancho para deconstruir el accesorio de la clase después de ejecutar todas las pruebas en la clase.

Banco de pruebas

Es una colección de conjuntos de pruebas, casos de prueba o ambos.

Corredor de prueba

Controla la ejecución de los casos de prueba o demandas y proporciona el resultado al usuario. Puede usar GUI o una interfaz de texto simple para proporcionar el resultado.

Example

El siguiente programa de Python usa el módulo unittest para probar un módulo llamado Fibonacci. El programa ayuda a calcular la serie de Fibonacci de un número. En este ejemplo, hemos creado una clase llamada Fibo_test, para definir los casos de prueba usando diferentes métodos. Estos métodos se heredan de unittest.TestCase. Usamos dos métodos predeterminados: setUp () y tearDown (). También definimos el método testfibocal. El nombre de la prueba debe comenzar con la letra de prueba. En el bloque final, unittest.main () proporciona una interfaz de línea de comandos para el script de prueba.

import unittest
def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a
class Fibo_Test(unittest.TestCase):
   def setUp(self):
   print("This is run before our tests would be executed")
   def tearDown(self):
   print("This is run after the completion of execution of our tests")

   def testfibocal(self):
   self.assertEqual(fib(0), 0)
   self.assertEqual(fib(1), 1)
   self.assertEqual(fib(5), 5)
   self.assertEqual(fib(10), 55)
   self.assertEqual(fib(20), 6765)

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

Cuando se ejecuta desde la línea de comando, el script anterior produce una salida que se ve así:

Salida

This runs before our tests would be executed.
This runs after the completion of execution of our tests.
.
----------------------------------------------------------------------
Ran 1 test in 0.006s
OK

Ahora, para que quede más claro, estamos cambiando nuestro código que ayudó a definir el módulo de Fibonacci.

Considere el siguiente bloque de código como ejemplo:

def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a

Se realizan algunos cambios en el bloque de código como se muestra a continuación:

def fibonacci(n):
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a

Ahora, después de ejecutar el script con el código modificado, obtendremos el siguiente resultado:

This runs before our tests would be executed.
This runs after the completion of execution of our tests.
F
======================================================================
FAIL: testCalculation (__main__.Fibo_Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unitg.py", line 15, in testCalculation
self.assertEqual(fib(0), 0)
AssertionError: 1 != 0
----------------------------------------------------------------------
Ran 1 test in 0.007s

FAILED (failures = 1)

La salida anterior muestra que el módulo no ha dado la salida deseada.

Módulo Docktest

El módulo docktest también ayuda en las pruebas unitarias. También viene empaquetado con Python. Es más fácil de usar que el módulo unittest. El módulo unittest es más adecuado para pruebas complejas. Para usar el módulo doctest, necesitamos importarlo. La cadena de documentación de la función correspondiente debe tener una sesión de Python interactiva junto con sus salidas.

Si todo está bien en nuestro código, entonces no habrá salida del módulo docktest; de lo contrario, proporcionará la salida.

Ejemplo

El siguiente ejemplo de Python usa el módulo docktest para probar un módulo llamado Fibonacci, que ayuda a calcular la serie Fibonacci de un número.

import doctest
def fibonacci(n):
   """
   Calculates the Fibonacci number

   >>> fibonacci(0)
   0
   >>> fibonacci(1)
   1
   >>> fibonacci(10)
   55
   >>> fibonacci(20)
   6765
   >>>

   """
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a
      if __name__ == "__main__":
   doctest.testmod()

Podemos ver que la cadena de documentación de la función correspondiente llamada fib tenía una sesión interactiva de Python junto con las salidas. Si nuestro código está bien, entonces no habrá salida del módulo doctest. Pero para ver cómo funciona podemos ejecutarlo con la opción –v.

(base) D:\ProgramData>python dock_test.py -v
Trying:
   fibonacci(0)
Expecting:
   0
ok
Trying:
   fibonacci(1)
Expecting:
   1
ok
Trying:
   fibonacci(10)
Expecting:
   55
ok
Trying:
   fibonacci(20)
Expecting:
   6765
ok
1 items had no tests:
   __main__
1 items passed all tests:
4 tests in __main__.fibonacci
4 tests in 2 items.
4 passed and 0 failed.
Test passed.

Ahora, cambiaremos el código que ayudó a definir el módulo de Fibonacci.

Considere el siguiente bloque de código como ejemplo:

def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a

El siguiente bloque de código ayuda con los cambios:

def fibonacci(n):
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a

Después de ejecutar el script incluso sin la opción –v, con el código modificado, obtendremos el resultado como se muestra a continuación.

Salida

(base) D:\ProgramData>python dock_test.py
**********************************************************************
File "unitg.py", line 6, in __main__.fibonacci
Failed example:
   fibonacci(0)
Expected:
   0
Got:
   1
**********************************************************************
File "unitg.py", line 10, in __main__.fibonacci
Failed example:
   fibonacci(10)
Expected:
   55
Got:
   89
**********************************************************************
File "unitg.py", line 12, in __main__.fibonacci
Failed example:
   fibonacci(20)
Expected:
   6765
Got:
   10946
**********************************************************************
1 items had failures:
   3 of 4 in __main__.fibonacci
***Test Failed*** 3 failures.

Podemos ver en la salida anterior que fallaron tres pruebas.