Evaluación 2

Testing y Calidad de Software

Entrega Incremental 2 - TDD, BDD y Cobertura

15%
Laboratorio
Fecha límite: 12 de marzo de 2026
Requisito Obligatorio

Esta evaluación REQUIERE la E1 (Dominio) completada. Debes tener el dominio funcionando para poder testearlo. Si tu E1 no funciona, no puedes hacer E2.

Clases Relacionadas

Estas clases preparan los conocimientos necesarios para esta evaluación:

Objetivo de Aprendizaje

Garantizar la calidad del dominio de TaskFlow mediante pruebas automatizadas con pytest (unitarios) y behave (BDD). Aplicar el ciclo TDD y alcanzar cobertura mínima del 80%.

Modalidad de Trabajo

Individual o en parejas (máximo 2 personas). Debe ser el mismo equipo de E1.

Formato de entrega: Repositorio GitHub con dominio de E1 + carpeta tests/ con todos los tests pytest + archivo .feature para behave. Incluir README.md con comando para ejecutar tests.

Requisitos Técnicos Detallados

1. Tests Unitarios con pytest

Descripción: Pruebas exhaustivas para todas las clases del dominio usando pytest.

Cobertura mínima requerida: 80%
Técnicas obligatorias:
Casos de prueba obligatorios por clase:
  • Usuario: creación válida, username inválido, email inválido, activar/desactivar
  • Proyecto: creación, agregar tarea, obtener tareas pendientes, filtrar por prioridad
  • Tarea: creación, cambiar estado, cambiar prioridad, completar
2. Fixtures y Datos de Prueba

Descripción: Crear fixtures reutilizables para evitar duplicación de código en tests.

Fixtures obligatorios en conftest.py:
Scopes a utilizar:
3. Tests BDD con behave

Descripción: Escenarios en lenguaje Gherkin que describen comportamiento del sistema.

Archivos .feature obligatorios:
Estructura de escenarios:
Mínimo 3 escenarios por archivo .feature
  • Escenario feliz (happy path)
  • Escenario de error (validación fallida)
  • Escenario de edge case (caso límite)
4. Cobertura de Código

Descripción: Medir y reportar cobertura de pruebas con pytest-cov.

Comando obligatorio:
Umbrales de cobertura:

Ejemplo de Implementación

tests/conftest.py
import pytest
from datetime import datetime
from src.domain.usuario import Usuario
from src.domain.proyecto import Proyecto
from src.domain.tarea import Tarea, PrioridadTarea, EstadoTarea


@pytest.fixture
def usuario_ejemplo() -> Usuario:
    """Fixture que provee un usuario válido para pruebas."""
    return Usuario(
        username="testuser",
        email="test@example.com",
        nombre_completo="Usuario de Prueba"
    )


@pytest.fixture
def proyecto_ejemplo(usuario_ejemplo: Usuario) -> Proyecto:
    """Fixture que provee un proyecto con líder."""
    return Proyecto(
        nombre="Proyecto Test",
        descripcion="Descripción de prueba",
        lider=usuario_ejemplo
    )


@pytest.fixture
def tarea_ejemplo() -> Tarea:
    """Fixture que provee una tarea pendiente."""
    return Tarea(
        titulo="Tarea de prueba",
        descripcion="Descripción de prueba",
        prioridad=PrioridadTarea.MEDIA
    )
tests/test_usuario.py
import pytest
from src.domain.usuario import Usuario


class TestUsuarioCreacion:
    """Tests para la creación de usuarios."""

    def test_crear_usuario_valido(self, usuario_ejemplo: Usuario):
        """Verifica que un usuario válido se crea correctamente."""
        assert usuario_ejemplo.username == "testuser"
        assert usuario_ejemplo.email == "test@example.com"
        assert usuario_ejemplo.activo is True

    @pytest.mark.parametrize("username_invalido,expected_msg", [
        ("ab", "al menos 3 caracteres"),
        ("user@name", "solo letras y números"),
        ("", "al menos 3 caracteres"),
    ])
    def test_username_invalido_lanza_error(
        self, username_invalido: str, expected_msg: str
    ):
        """Verifica que usernames inválidos lanzan ValueError."""
        with pytest.raises(ValueError) as exc_info:
            Usuario(username=username_invalido, email="test@test.com")
        assert expected_msg in str(exc_info.value)

    def test_email_invalido_lanza_error(self):
        """Verifica que emails sin @ lanzan ValueError."""
        with pytest.raises(ValueError, match="Email inválido"):
            Usuario(username="testuser", email="emailinvalido.com")

Estructura de Entrega

Organización de Archivos Obligatoria
src/ # Dominio de E1 (ya debe existir) ├── __init__.py └── domain/ ├── __init__.py ├── enums.py ├── usuario.py ├── proyecto.py └── tarea.py tests/ # Tests unitarios pytest ├── __init__.py ├── conftest.py # Fixtures compartidas ├── test_usuario.py # Tests de Usuario ├── test_proyecto.py # Tests de Proyecto ├── test_tarea.py # Tests de Tarea └── test_enums.py # Tests de Enums features/ # Tests BDD behave ├── environment.py # Configuración de behave ├── steps/ │ ├── __init__.py │ ├── usuarios_steps.py │ ├── proyectos_steps.py │ └── tareas_steps.py ├── usuarios.feature # Escenarios de usuarios ├── proyectos.feature # Escenarios de proyectos └── tareas.feature # Escenarios de tareas pytest.ini # Configuración de pytest .coveragerc # Configuración de cobertura requirements.txt # pytest, pytest-cov, behave README.md # Cómo ejecutar los tests

Pasos para Completar la Evaluación

1Configurar entorno de testing

Instalar pytest, pytest-cov, behave y crear pytest.ini

2Crear fixtures en conftest.py

Usuario, proyecto, tarea y combinaciones

3Implementar tests de Usuario

Creación, validaciones, activar/desactivar con parametrize

4Implementar tests de Tarea

Estados, prioridades, completar

5Implementar tests de Proyecto

Agregar tareas, filtrar, relaciones

6Crear escenarios BDD

Archivos .feature y steps correspondientes

7Verificar cobertura ≥80%

Ejecutar pytest --cov y ajustar hasta alcanzar

Rúbrica de Evaluación (100%)

Criterio Excelente (5.0) Aceptable (3.5) Insuficiente Peso
Cobertura ≥80% ≥80% con tests significativos que prueban lógica real 70-79% o tests superficiales <70% o sin medir cobertura 30%
Tests Unitarios Fixtures, parametrize, raises, setup/teardown bien usados Tests básicos sin técnicas avanzadas Sin pytest o tests vacíos 25%
Tests BDD 3+ escenarios Gherkin claros, steps implementados Escenarios básicos o pasos incompletos Sin behave o .feature vacíos 20%
Integración E1 Tests pasan contra dominio real de E1 Algunos tests fallan por dominio incompleto Dominio E1 no funciona 15%
Documentación README con comandos, requisitos, cobertura README básico sin instrucciones claras Sin README o vacío 10%
Penalizaciones
Conexión con E3

El dominio testeado aquí se expondrá vía web en E3: Prototipo Web. Asegúrate de que todos los tests pasen antes de continuar.

← E1: Dominio E3: Web →