Pruebas de software en el contexto de la analítica y la ciencia de datos

Glei Montoya
Orca Labs
Published in
5 min readMay 23, 2023
Photo by Marvin Meyer on Unsplash

¿Qué son las pruebas de software, y por qué son necesarias?

El foco principal de las pruebas de software, es garantizar cierto nivel de estabilidad en el desarrollo. Queremos que nuestros productos de datos, continúen funcionando consistentemente luego de que un nuevo cambio es introducido al código que ya tenemos en producción. Cada que se agrega funcionalidad al producto, sin sus respectivas pruebas, cabe la posibilidad de introducir bugs y que se alteren las funcionalidades que ya estaban funcionando correctamente. Las pruebas que se diseñan, permiten identificar inmediatamente un funcionamiento no esperado del código que se está agregando y por lo tanto disminuye considerablemente el esfuerzo y el costo de solucionar el problema. A su vez, las pruebas unitarias son una forma de documentación, que permite identificar cómo se usa el código que el desarrollador construyó.

Tipos de pruebas

Pruebas Unitarias

Las pruebas unitarias son la unidad básica de pruebas, trata de validar el funcionamiento de una unidad de software como una clase o una función, independientemente del sistema que lo contiene. Las pruebas unitarias se usan como una forma para asegurar que el módulo o la función se comportan exactamente de la misma manera en que fue diseñada. Son la base del desarrollo orientado por pruebas (TDD — Test Driven Development)

Pruebas de Integración

Los componentes individuales que pasan las pruebas unitarias son ensamblados en componentes mas grandes. Las pruebas de integración, prueban estos componentes más grandes con técnicas como el mocking, que permite emular el comportamiento de componentes externos y complejos para usarlos en las pruebas de un componente. Por ejemplo la conexión a un base de datos, o una conexión a un componente de nube.

Pruebas de sistema

Son pruebas que se corren a nivel de toda la aplicación. Pueden agruparse en pruebas de humo, que son pruebas muy sencillas que verifican un funcionamiento crítico; Pruebas de performance, que tratan de validar el funcionamiento de la aplicación bajo estrés; y pruebas de regresión, en las que se incluye el histórico de pruebas que se han diseñado, para evitar que se repitan errores que ya se corrigieron en el pasado.

¿Qué debemos probar como ingenieros y científicos de datos?

Los productos de datos como cualquier producto de software tiene unos componentes particulares, que deben tener pruebas pensando en garantizar su estabilidad y considerar implementaciones a través de integración y despliegue continuo.

En ingeniería de datos (ver https://www.kdnuggets.com/2020/08/unit-test-data-pipeline-thank-yourself-later.html)

  • Cargas de datos desde cualquier fuente
  • Funciones de transformación de datos
  • Validación de datos (Distribuciones, esquemas, etc)
  • Dags Airflow
  • Operadores Dataflow
  • etc

En entrenamiento de modelos y despliegues en producción (ver https://dssg.github.io/hitchhikers-guide/curriculum/programming_best_practices/test-test-test/ds_testing/)

  • Ingeniería de características
  • Especificaciones del modelo (por ejemplo, definiciones de hiperparámetros)
  • Reproducibilidad del entrenamiento
  • Validación de entradas al modelo
  • Pruebas de integración de los pipelines
  • Pruebas a las apis
  • Valores esperados de inferencia
  • Validación de datos (distribuciones, esquemas, etc)

¿Cuántas pruebas son suficientes?

El mecanismo con el que contamos para saber qué tan confiable es mi código, es la cobertura de las pruebas. Es una métrica que indica qué porcentaje de mi código está cubierta por al menos una prueba. Esta métrica se usa como un criterio de calidad en los pipelines de integración continua, donde el código no es puesto en producción a no ser que supere cierto umbral. En la metodología Test Driven Development, se procura construir la prueba antes que el código asociado, por lo que siguiendo la metodología tenemos una métrica de cobertura por lo general, muy alta (ver https://towardsdatascience.com/tdd-datascience-689c98492fcc).

Diseñando pruebas con pytest

La estructura del proyecto debe tener una estructura similar a la que se muestra a continuación, considerando que los scripts con las pruebas van en la carpeta tests y los nombres de archivo de las pruebas, deben iniciar con test_ para que puedan ser identificadas por pytest para su ejecución.

tweet_project/
├─ tweet_project/
│ ├─ __init__.py
│ ├─ tweet_cleaning.py
├─ tests/
│ ├─ __init__.py
│ ├─ test_tweet_project.py
├─ notebooks/
│ ├─ notebook1.ipynb
│ ├─ notebook2.ipynb
├─ .gitignore
├─ setup.py
├─ README.md

El paquete tweet_project contiene un módulo tweet cleaning que contiene funciones para limpiar un tweet. Por ejemplo:

#tweet_cleaning.pyimport redef clean_mentions(tweet):
return re.sub(“@[\w]*”, “”, tweet)

def detect_retweet(tweet):
return tweet.startswith(“RT:”)
def clean_nonalphanumeric(tweet):
return re.sub(“[^a-zA-Z0–9]”, “”, tweet)
def detect_empty_tweets(tweet):
return tweet == “”

La prueba unitaria correspondiente a esta función, estaría consignada en tests/test_tweet_project.py y tendría mas o menos lo siguiente:

import pytest@pytest.fixture
def dirty_tweets():
"""
Tweets with problems that need to be cleaned:
- Delete mentions --> @xxxx
- Detect retweets --> Starting with "RT:"
- Clean all non-alphanumeric characters
- Detect empty tweets --> Might happen after cleaning
"""

problematic_tweets = ["@Somebody",
"RT: Now you're just @Somebody that I used to know",
"あなたは私が知っていた誰かです",
""]
return problematic_tweets

from tweet_project import tweet_cleaning
def test_mentions_gone(dirty_tweets):
test_case = dirty_tweets[0]
assert tweet_cleaning.clean_mentions(test_case) == ""

def test_detect_rt(dirty_tweets):
test_case = dirty_tweets[1]
assert tweet_cleaning.detect_retweet(test_case)
def test_nonalphanumeric_gone(dirty_tweets):
test_case = dirty_tweets[2]
assert tweet_cleaning.clean_nonalphanumeric(test_case) == ""
def test_detect_empty_tweets(dirty_tweets):
test_case = dirty_tweets[3]
assert tweet_cleaning.detect_empty_tweets(test_case)

En pytest se usan los fixtures para establecer unas condiciones iniciales que se pueden usar en diferentes pruebas, en este caso se usan para definir una lista con unos tweets que pueden tener características que se quieren limpiar con el código que estamos desarrollando. Se puede ver en el código que la función dirty_tweets está decorada con @pytest.fixture y esta función es usada como herencia en la definición de la función de la prueba test_mentions_gone.

Al momento de ejecutar la pruebas, pytest se encarga de verificar que la sentencia assert que fue definida en cada prueba, se cumpla, y de ser así se considera exitosa la prueba. Podemos ver además en el resultado el nivel de cobertura de las pruebas que realizamos.

gleiwer@huxley  ~/Code/Learning/tweet_project  pytest -v -o junit_family=xunit1  --cov tweet_project --junitxml=./results.xml
========================================================== test session starts ==========================================================
platform darwin -- Python 3.8.12, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- /Users/gleiwer/.virtualenvs/pqrs_v3/bin/python
cachedir: .pytest_cache
rootdir: /Users/gleiwer/Code/Learning/tweet_project
plugins: env-0.6.2, mock-3.6.1, typeguard-2.12.1, cov-3.0.0
collected 4 items
tests/test_tweet_project.py::test_mentions_gone PASSED [ 25%]
tests/test_tweet_project.py::test_detect_rt PASSED [ 50%]
tests/test_tweet_project.py::test_nonalphanumeric_gone PASSED [ 75%]
tests/test_tweet_project.py::test_detect_empty_tweets PASSED [100%]
------------------------------ generated xml file: /Users/gleiwer/Code/Learning/tweet_project/results.xml ----------------------------------------- coverage: platform darwin, python 3.8.12-final-0 ----------
Name Stmts Miss Cover
-----------------------------------------------------
tweet_project/__init__.py 0 0 100%
tweet_project/tweet_cleaning.py 9 0 100%
-----------------------------------------------------
TOTAL 9 0 100%
=========================================================== 4 passed in 0.13s ===========================================================

--

--

Glei Montoya
Orca Labs
Editor for

Sr Machine learning engineer en mercado libre, curioso sobre la ciencia, el arte, la productividad y el código fuente. Estudiante perpetuo.