Práctica — Funciones, calidad de código y pruebas unitarias

Por Jose R. Zapata

Ultima actualización: 15/Ago/2026

Invítame a un Café

Objetivo

Escribir funciones de Python relacionadas con su proyecto, aplicando buenas prácticas (nombres descriptivos, type hints, docstrings), verificando la calidad de código con pre-commit (ruff + mypy), y cubriéndolas con pruebas unitarias (pytest) antes de integrarlas mediante un Pull Request.

Criterio de éxito: un módulo src/.../funciones_<su_tema>.py con al menos 3 funciones propias (tipadas y documentadas) relacionadas con su dataset, un módulo tests/.../test_funciones_<su_tema>.py con pruebas unitarias que pasen, pre-commit corriendo sin errores sobre los archivos nuevos, y un Pull Request abierto con el trabajo.

Prerequisitos

Esta práctica no parte de cero: use su propio repositorio de proyecto (el creado desde el template para su dataset de E1), no un repositorio nuevo vacío.

Paso 1 — Crear una rama de trabajo

Como en la práctica de esta mañana, cree una rama:

git checkout main
git pull origin main
git checkout -b feature/funciones-datos

Paso 2 — Preparar el ambiente y pre-commit

Active el ambiente virtual del proyecto e instale/actualice dependencias:

source .venv/bin/activate
uv sync

El proyecto (generado desde el template) ya trae .pre-commit-config.yaml, instale los hooks localmente:

uv run pre-commit install

Verifique que existen los archivos de configuración de calidad de código (los trae el template):

  • .pre-commit-config.yaml
  • .code_quality/ruff.toml
  • .code_quality/mypy.ini

Si no existen en su repositorio, revise Calidad de Código y Pre-commit para crearlos antes de continuar.

Paso 3 — Escribir las funciones

Cree un archivo de funciones dentro de src/ (por ejemplo src/limpieza/funciones_datos.py) con al menos 3 funciones propias relacionadas con su dataset de E1 (por ejemplo: limpiar una columna, calcular una métrica descriptiva, detectar outliers, normalizar una variable, validar un rango de valores, etc.).

Cada función debe tener:

  • Nombre descriptivo (verbo + qué hace).
  • Type hints en parámetros y valor de retorno.
  • Docstring (Args, Returns, y opcionalmente Examples) siguiendo el estilo visto en Funciones en Python.

Ejemplo de referencia (no copiar literal — adapte la idea a su propio dataset):

import pandas as pd


def calcular_valores_nulos_por_columna(datos: pd.DataFrame) -> pd.Series:
    """Calcula el número de valores nulos por columna de un DataFrame.

    Args:
        datos (pd.DataFrame): DataFrame de entrada.

    Returns:
        pd.Series: Conteo de valores nulos por columna.

    Examples:
        >>> import pandas as pd
        >>> df = pd.DataFrame({"a": [1, None, 3], "b": [None, None, 3]})
        >>> calcular_valores_nulos_por_columna(df)["a"]
        1
    """
    return datos.isna().sum()
No es obligatorio usar pandas — depende de su dataset y de lo que sus funciones necesiten (puede ser sobre listas, diccionarios, arreglos de numpy, etc.). Lo importante es que las funciones sean relevantes para su proyecto.

Paso 4 — Verificar calidad de código con pre-commit

Ejecute pre-commit sobre los archivos que creó (o sobre todo el repositorio) y corrija lo que reporte ruff (lint + format) y mypy (static typing) hasta que pase sin errores:

uv run pre-commit run --files src/limpieza/funciones_datos.py
uv run pre-commit run --all-files

Si ruff reporta errores de formato o lint, en general puede corregirlos automáticamente agregando --fix a la configuración ya definida en .pre-commit-config.yaml (ver Calidad de Código y Pre-commit); los errores de mypy (tipado) debe corregirlos usted mismo ajustando las anotaciones de tipo.

Paso 5 — Escribir pruebas unitarias

Cree el archivo de pruebas correspondiente en tests/ (ver la convención de Pruebas Unitarias en Python: un directorio tests espejo de src, con __init__.py, y archivos/funciones que empiecen con test_).

Para cada una de sus 3+ funciones, escriba al menos:

  • Una prueba con un caso “normal” (entrada típica, salida esperada conocida).
  • Una prueba de caso límite o de error (ej. entrada vacía, valores inválidos, división por cero, etc. — lo que aplique a su función).
import pandas as pd

from src.limpieza.funciones_datos import calcular_valores_nulos_por_columna


def test_calcular_valores_nulos_por_columna() -> None:
    """Caso normal: DataFrame con nulos conocidos."""
    df = pd.DataFrame({"a": [1, None, 3], "b": [None, None, 3]})
    resultado = calcular_valores_nulos_por_columna(df)
    assert resultado["a"] == 1
    assert resultado["b"] == 2


def test_calcular_valores_nulos_por_columna_sin_nulos() -> None:
    """Caso límite: DataFrame sin valores nulos."""
    df = pd.DataFrame({"a": [1, 2, 3]})
    resultado = calcular_valores_nulos_por_columna(df)
    assert resultado["a"] == 0

Ejecute las pruebas y confirme que todas pasan:

uv run pytest -v --cov=src

Paso 6 — Commit, push y Pull Request

Haga commits siguiendo Conventional Commits (uno para las funciones, otro para las pruebas, o combinados si el cambio es pequeño):

git add src/limpieza/funciones_datos.py
git commit -m "feat: agregar funciones de procesamiento de datos"

git add tests/limpieza/test_funciones_datos.py
git commit -m "test: agregar pruebas unitarias para funciones de procesamiento de datos"
Si pre-commit está instalado (Paso 2), se ejecutará automáticamente en cada git commit y puede bloquear el commit si encuentra errores de calidad de código — corríjalos y vuelva a intentar el commit.

Suba la rama y abra el Pull Request:

git push origin feature/funciones-datos

Abra el Pull Request en GitHub enlazando el issue del Paso 1 en la descripción (ver Pull Request — Qué es y buenas prácticas).

Checklist de pasos

  • rama creada
  • pre-commit instalado (pre-commit install) en el repositorio del proyecto
  • Al menos 3 funciones propias en src/, relacionadas con el dataset, con type hints + docstrings
  • pre-commit run pasa sin errores sobre los archivos nuevos (ruff lint, ruff format, mypy)
  • Archivo de pruebas en tests/ con al menos 1 caso normal + 1 caso límite por función
  • pytest corre y todas las pruebas pasan
  • Commits siguiendo Conventional Commits
  • Pull Request abierto, enlazado al issue

Referencias

Phd. Jose R. Zapata