Herramientas de testing en Python

El testing es una de las fases más importantes en el ciclo de desarrollo en nuestra aplicación, este nos ayuda a validar que nuestro código cumple con los requisitos para los que ha sido diseñado.

Muchas veces obviamos el testing como fase de nuestro desarrollo porque pensamos que este reduce la velocidad en la que implementamos nuevas funcionalidades, pero este realmente nos ayuda a mantener nuestra velocidad de desarrollo a medio y largo plazo, al mismo tiempo que nos ayuda a reducir el número de bugs e incidencias que suceden en nuestra aplicación, además de ayudarnos a resolver más rápido aquellos que nos encontramos.

Tipos de Testing

Dentro del testing nos encontramos varios tipos de testing que nos ayudan a validar nuestra aplicación, la estructura de testing más famosa es la que aparece en la siguiente imagen:

Imagen 0 en Herramientas de testing en Python

UI Tests: Son aquellos que prueban todo el flujo de nuestra aplicación, en las aplicaciones típicas como escritorio o web, prueban la aplicación como una caja negra, desde la misma interfaz gráfica, utilizando herramientas como Cypress o Selenium.

Services Tests/ IntegrationTests: Este tipo de testing se encarga de validar funcionalidades a nivel de servicio en su conjunto, o cómo se integran diferentes partes de nuestra aplicación. Estos tests son muy útiles para probar partes aisladas como nuestros endpoints, o la conexión de nuestros repositorios con la base de datos.

Unit Tests: Los Unit Tests son la base de nuestro ciclo de testing, son los tests más rápidos de ejecutar por lo que pueden darnos feedback de forma muy rápida. Estos tests se encargan de validar las unidades mínimas de nuestro código, como puede ser el comportamiento de una clase o de un método.

Cómo decíamos, el testing es una pieza fundamental en nuestro desarrollo de software, por esto se ha ido refinando en los últimos años, hasta incluir el testing en muchas otras fases de nuestra aplicación, como la puesta en producción:

Imagen 1 en Herramientas de testing en Python

En el esquema anterior, vemos muchos tipos diferentes de testing, no todos son necesarios en todos los proyectos, pero en función de la complejidad, y la lógica del mismo podríamos tener que llevarlos a cabo.

Qué se puede hacer con Python para testing

Una vez hemos entendido la importancia del testing, no podemos considerar un lenguaje de programación moderno como “production ready”, o apto para un proyecto profesional a no ser que tengamos herramientas de testing a nuestra disposición.

Python es un excelente ejemplo de ello, ya que Python no solo brilla por la potencia y sencillez del lenguaje, si no por las grandes herramientas de testing que trae incluidas, y por las diferentes funcionalidades que ha incluido la comunidad en forma de librerías extra.

Para los que aún no hayáis podido aprender cómo funciona Python os recomiendo empezar con este curso de Python 3, que explica con sencillos ejercicios cómo empezar a trabajar con este potente lenguaje.

Durante este artículo veremos las herramientas más populares para testing que tiene Python, sus ventajas y desventajas y como utilizarlas.

Unittest (pyTest)

Unittest de las herramientas más populares en Python, y que sin duda todos los desarrolladores de Python hemos oído hablar, ya que es la herramienta que viene incluida con el lenguaje.

Ejemplo

from unittest import TestCase

class Math:
    def sum(num1: int, num2: int) -> int:
        return num1 + num2

class TestMath(TestCase):
    def setUp(self)
        # Este código se ejecutará antes de cada test, y nos ayuda a preparar
        # nuestro un escenario genérico sobre el que ejecutar nuestro testing.
        self.math = Math()

    def test_sum_is_working(self):
        self.assertEqual(self.math.sum(1, 2), 3)

En el ejemplo anterior, vemos como utilizamos el método setUp para configurar el escenario por defecto para cada uno de nuestros tests, y dentro del método test_sum_is_working probamos que el método suma de nuestra clase Math está funcionando como debe.

Tipo: Unitario

Ventajas

  • Viene incluido con el lenguaje y es uno de los más usados en los proyectos Python.
  • Suele tener compatibilidad con el resto de librerías como Pytest, por lo que se recomienda empezar con él.
  • Tiene incluido un servicio de discovery que permite encontrar y ejecutar lo tests automáticamente.
  • Muy sencillo de utilizar, tiene algunos métodos que controlan el ciclo de vida de tus tests.

Desventajas

  • Funcionalidades limitadas para complementar el testing cómo poder saber el coverage de tu aplicación
  • Sin extensiones o sistema de plugins para completar su funcionalidad cómo tiene pytest.

Pytest

Pytest es una herramienta para test unitario, una de las más utilizadas por detrás de unittest. Es una herramienta muy potente para testing unitario, altamente customizable, y con muchos plugins con todo tipo de usos, además de features como fixtures, o el archivo conftest, que nos permite hacer un setup global para tu suite de testing.

Ejemplo

class Math:
    def sum(self, num1: int, num2: int) -> int:
        return num1 + num2

@pytest.fixture
def math() -> Math:
    # Este método devolverá un objeto Math cada vez que sea pedido por una función de pytest.
    return Math()

@pytest.parametrize(((1,2,3), (2,2,4)))
def test_sum(math, num1, num2, result):
    # El decorador parametrize nos ayuda a probar diferentes escenarios de forma automática.
    assert math.sum(sum1, sum2) == result

En el ejemplo vemos, como utilizamos las fixtures de pytest para devolver una nueva clase Math siempre que sea necesario, y usando el decorador parametrize, podemos decirle a nuestro test los diferentes escenarios que queremos testear de forma dinámica.

Tipo: Unitario

Ventajas

  • Altamente extensible, tiene un montón de plugins y extensiones desarrolladas por la comunidad, que, si una funcionalidad no está incluida, seguramente habrá un plugin que la implemente.
  • La curva de aprendizaje no es muy alta, en parte por la buena documentación.
  • Compatible con la sintaxis de unittest.
  • Herramienta realmente potente de discovery automático para tus tests.

Desventajas

  • Ha medida que trabajas con la librería y quieres profundizar, la documentación empieza a ser más confusa.
  • Muchos plugins en desuso y que no han sido mantenidos por sus autores, por lo que tienes que tener cuidado de cuales utilizas.

Hypothesis

Hypothesis es una librería que se apoya en la filosofía “property based testing”, básicamente hace uso de una serie de estrategias que definimos, para generar escenarios pseudo-aleatorios en los que ejecutar nuestros tests, de esta forma nos aseguramos la solidez de nuestro código.

Las estrategias de Hypothesis están desarrolladas de tal forma que tratan de estresar nuestra aplicación lo máximo posible, viendo si es capaz de soportar todos, por ejemplo, para el caso de las estrategias de números: -1, 0, -191932349232, 191932349232…

Tipo: Unitario

Ejemplo

from hypothesis import given, strategies as st

class Math:
    def sum(self, num1: int, num2: int) -> int:
        return num1 + num2

@given(num1=integers(), num2=integers())
def test_decode_inverts_encode(num1: int, num2: int):
        # Las strategies en hypothesis son los que se encargan de la generación aleatoria de los inputs
        # en nuestro testing.
        math = Math()
    assert math.sum(num1, num2) == num1 + num2

Ventajas

  • Muy sencillo de utilizar sobre todo para casos de uso básicos
  • La comunidad le ha mostrado mucho apoyo, y varias empresas grandes han escrito artículos sobre como la utilizan para añadir resiliencia a sus aplicaciones.

Desventajas

  • Para probar casuísticas algo complejas, puedes necesitar bastante tiempo preparando el test.
  • Curva de aprendizaje elevada, debido sobre todo al cambio de paradigma en nuestro testing.

Schemathesis

Schemathesis es una herramienta basada en hypothesis tremendamente útil para probar nuestras APIs en base a una definición de OpenAPI.

Schemathesis se encarga de recoger una definición que tenemos en OpenAPI y estresar nuestra REST API con valores adecuados en función de la especificación que le hayamos pasado, simplificando nuestro proceso de testing.

Ejemplo

st run https://example.schemathesis.io/openapi.json

Tipo: Service

Ventajas:

  • Muy sencillo de utilizar, solo tenemos que indicarle dónde está la definición OpenAPI y el host donde se encuentra.
  • Realmente útil para probar casos muy variados en nuestra API, que no te sorprenda que al primer intento acabe por lanzarte algún 500 que no tuvieses en cuenta.

Desventajas

  • La herramienta necesita sí o sí una definición OpenAPI para funcionar.
  • Poca flexibilidad para configurar el comportamiento de tu herramienta.
  • Comunidad de momento pequeña, pero creciendo.

Playwright

Playwright es una herramienta desarrollada por Microsoft, con una gran comunidad, esta nos ayuda a poder hacer tests End to End en aplicaciones webs, tal y como podríamos hacer con otras herramientas como Selenium o Puppeter. Con ella podemos cargar una página e imitar el comportamiento que tendría un usuario en un navegador convencional.

Ejemplo

import re
from playwright.sync_api import Page, expect

def test_openwebinars(page: Page):
    page.goto("https://openwebinars.net/")

    expect(page).to_have_title(re.compile("OpenWebinars.net"))

    heading = page.locator('h1')

    expect(heading).to_have_text("Formación tecnológica para llevar a tu equipo al siguiente nivel")

En este ejemplo probamos que la página de OpenWebinars sigue teniendo un título h1 con un texto específico en su home page.

**Tipo:** End to End

Ventajas

  • Gran documentación, es muy sencillo empezar a hacer testing de tus páginas webs.
  • Curva de aprendizaje reducida, en parte gracias a la documentación, y en parte gracias a lo sencilla que es de utilizar su API.
  • Playwright te instala herramientas propias para depurar aplicaciones que hace que sea muy sencillo solucionar problemas.
  • Versátil, Playwright se amolda a otros escenarios, como hacer testing de APIs de forma directa

Desventajas

  • Como casi todas las herramientas de test End to End, conforme nuestros tests van creciendo, el tiempo de testing tiende a crecer bastante.

Robot

Robot es una herramienta que nos permite hacer testing en base a comportamientos, usando la filosofía Behavior Driven Development (BDD). Este paradigma de testing se basa en que nuestro código debería ser siempre evaluado en función del comportamiento deseado, sin llegar a comprender cómo funciona el código que lo implementa.

Robot se compone de tres tipos de archivos que le dan toda la información necesaria:

  • Robot: Archivo que contiene los escenarios que se van a evaluar
  • Resource: Archivo con el mapping de las operaciones en “lenguaje natural” y las operaciones a ejecutar ene l código, así como los parámetros que necesitamos para cada operación.
  • Código python: Archivo con el código en python que implementa el test real.

Ejemplo

Calculator_Test_Suite.robot

*** Settings ***
Resource        Calculator_keywords.resource

*** Test Cases ***
Test Calculator
    Given The Calculator Is Running
    When The User Enters The Term "1 + 1"
    And The User Triggers The Calculation
    Then The Result Should Be "2"

Calculator_keywords.resource

*** Settings ***
Library         calculator.py

*** Keywords ***
The Calculator Is Running
    Log    Opening Calculator
    Start Calculator

The User Enters The Term "${term}"
    Log    Entering ${term}
    Set Test Variable    ${term}

The User Triggers The Calculation
    Log    Triggering Calculation
    ${result}    Calculate Term    ${term}
    Set Test Variable   ${result}

The Result Should Be "${expected}"
    Log    Checking Result
    Should Be Equal As Numbers    ${result}    ${expected}

calculator.py

def start_calculator():
    print("Empezando calculadora")

def calculate_term(term):
    return eval(term)

Tipo: Acceptante Testing

Ventajas

  • Es una librería muy utilizada para testing y RPA, con muy buena comunidad que la respalda.
  • Permite hacer test de aceptación en base a comportamiento de forma sencilla.
  • Gracias al uso de lenguaje natural, entender el código a partir de los tests se convierte en una tarea casi trivial.
  • El uso de palabras clave nos ayuda a esquematizar la funcionalidad, y el código suele acaba siendo más simple.

Desventajas

  • Curva de aprendizaje un poco elevada debido a la nueva estructura de ficheros.

Locust

Locust es una herramienta de Load Testing que nos ayuda a inyectar cantidades importantes de tráfico en nuestra aplicación, de forma que podemos comprobar cómo esta se comportaría bajo esas condiciones.

Locust además nos provee con una interfaz web desde la que podemos configurar nuestros experimentos, pararlos o cambiarlos, además de generar informes gráficos fácilmente.

Imagen 2 en Herramientas de testing en PythonImagen 3 en Herramientas de testing en Python

Tipo: Load Testing

Ejemplo

# locust.py

from locust import HttpUser, task

class MathUser(HttpUser):
    @task
    def sum(self):
        self.client.get("/sum")
                self.client.get("/divide")

En este ejemplo configuramos la tarea que nuestro usuario matemático hará sobre nuestra API, en este caso las acciones de sumar y dividir.

Ventajas

  • Mucha comunidad detrás, se está convirtiendo en una herramienta muy popular.
  • Muy sencilla de utilizar con muy buena documentación.
  • Te permite hacer pruebas de carga de forma distribuida, de forma que puedes utilizar un cluster de locust para tus pruebas de carga de forma muy sencilla.
  • Sencillo generar test de carga en función de comportamientos de usuarios.

Desventajas

  • No te permite demasiados cambios utilizando archivos de configuración, casi todo el comportamiento que quieras simular es en base implementación de código.
  • Aunque es sencillo hacer tests de carga en base a comportamientos, se añade un poco de complejidad para hacer tareas muy sencillas como hacer tráfico para un solo endpoint.

Conclusión

La realización de un testing efectivo, es complicado, pero tremendamente necesario para validar el correcto funcionamiento de nuestras aplicaciones, durante este artículo hemos visto como Python es un lenguaje que incluye todo tipo de herramientas muy potentes para testing de forma nativa, además cuenta con una comunidad que lo complementa, consiguiendo implementar nuestra Pirámide del Testing de forma satisfactoria. En caso de querer ver cómo se utilizan estas herramientas más en detalle y de forma práctica, os recomiendo el Taller de herramientas de testing para Python.

También te puede interesar...

Herramientas de Testing para Python

Herramientas de Testing para Python

49 minutos y 30 segundos · taller

  • Testing
Testing en Python

Testing en Python

50 minutos y 55 segundos · taller

  • Backend
Introducción al testing

Curso de introducción al testing

2 horas y 44 minutos · curso

  • Testing

Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más Información.