Files
TallerCiCd/frontend/TESTS.md
2026-02-04 13:20:29 +01:00

7.3 KiB

Tests del Frontend - Documentación

Este documento describe los tests implementados para el frontend del taller CI/CD.

Estructura de Tests

frontend/src/
├── utils/
│   ├── text.js           # Función prettify()
│   └── text.test.js      # Tests unitarios
├── services/
│   ├── api.js            # Funciones de API
│   └── api.test.js       # Tests con mocking
├── App.svelte            # Componente principal
└── App.test.js           # Tests de componente

Tecnologías Utilizadas

Herramienta Propósito
Vitest Framework de testing (compatible con Vite)
@testing-library/svelte Renderizado de componentes Svelte
jsdom Simulación del DOM en Node.js

1. Tests Unitarios: text.test.js

Objetivo

Testear funciones puras sin efectos secundarios. La función prettify() convierte strings en formato snake_case a Title Case.

Tests Implementados

Test Input Output Esperado Concepto
Conversión básica 'hello_world' 'Hello World' Transformación de texto
Múltiples palabras 'cafe_con_leche' 'Cafe Con Leche' Manejo de múltiples _
Sin underscore 'bocadillo' 'Bocadillo' Edge case
String vacío '' '' Boundary testing
Mayúsculas 'TOSTADA_INTEGRAL' 'Tostada Integral' Normalización

Código Explicado

import { describe, it, expect } from 'vitest';
import { prettify } from './text';

describe('prettify', () => {
    it('convierte snake_case a Title Case', () => {
        // Dado un string con underscores
        const input = 'hello_world';
        
        // Cuando llamamos a prettify
        const result = prettify(input);
        
        // Entonces obtenemos Title Case
        expect(result).toBe('Hello World');
    });
});

¿Por qué este test es ideal para principiantes?

  1. Sin dependencias externas: No necesita mocking
  2. Determinístico: Mismo input = mismo output siempre
  3. Fácil de romper: Cambiar la función hace fallar el test inmediatamente
  4. Concepto claro: Input → Función → Output

2. Tests de API: api.test.js

Objetivo

Testear funciones que hacen llamadas HTTP sin depender de un servidor real.

Concepto Clave: Mocking

Mocking significa "simular" una dependencia externa. En este caso, simulamos fetch():

beforeEach(() => {
    // Reemplazamos fetch global con una función simulada
    vi.stubGlobal('fetch', vi.fn());
});

afterEach(() => {
    // Restauramos el fetch original
    vi.unstubAllGlobals();
});

Tests Implementados

Test Qué Verifica Concepto de Testing
getMenu() éxito Devuelve datos cuando API OK Happy path
getMenu() error Lanza excepción cuando API falla Error handling
getPrices() items Extrae .items del response Data transformation
getPrices() vacío Devuelve [] si no hay items Defensive programming
getCiStatus() Llama a /health Contract testing
getBuildHistory() Llama a /builds Contract testing

Código Explicado

describe('getMenu', () => {
    it('devuelve datos del menu cuando la API responde correctamente', async () => {
        // 1. ARRANGE: Preparamos el mock
        const mockMenu = { starters: ['Sopa'], mains: ['Pescado'] };
        
        fetch.mockResolvedValueOnce({
            ok: true,
            json: () => Promise.resolve(mockMenu),
        });

        // 2. ACT: Ejecutamos la función
        const result = await getMenu();

        // 3. ASSERT: Verificamos el resultado
        expect(fetch).toHaveBeenCalledWith('/taller/api/menu');
        expect(result).toEqual(mockMenu);
    });
});

¿Por qué este test es importante para CI/CD?

  • Independencia: No necesita backend corriendo
  • Rapidez: Se ejecuta en milisegundos
  • Fiabilidad: No falla por problemas de red
  • Contract testing: Verifica que la API se llama correctamente

3. Tests de Componente: App.test.js

Objetivo

Verificar que el componente Svelte renderiza correctamente y muestra la información esperada.

Concepto Clave: Testing Library

@testing-library/svelte renderiza componentes y permite buscar elementos como lo haría un usuario:

import { render, screen, waitFor } from '@testing-library/svelte';
import App from './App.svelte';

// Renderiza el componente
render(App);

// Busca elementos como un usuario
screen.getByText('Menú del día');
screen.getByRole('heading', { level: 1 });

Tests Implementados

Test Qué Verifica Selector Usado
Título principal H1 existe getByRole('heading', { level: 1 })
Secciones menú Primeros/Segundos/Postres getByText('Primeros')
Estado API Muestra "Operativa" getByText(/Operativa/)
Build number Muestra "#42" getByText('#42')
Precios Muestra "Cafe", "Tostada" getByText('Cafe')

Código Explicado

describe('App.svelte', () => {
    beforeEach(() => {
        // Mockeamos fetch para cada endpoint
        vi.stubGlobal('fetch', createMockFetch());
    });

    it('muestra las secciones del menu cuando carga', async () => {
        // Renderizamos el componente
        render(App);

        // Esperamos a que cargue (es async)
        await waitFor(() => {
            expect(screen.getByText('Primeros')).toBeInTheDocument();
            expect(screen.getByText('Segundos')).toBeInTheDocument();
        });
    });
});

¿Por qué waitFor?

El componente hace llamadas async al montarse. waitFor espera hasta que:

  1. La condición se cumple, O
  2. Pasa el timeout (falla el test)

Ejecutar los Tests

Localmente

cd frontend
npm install
npm test

En modo watch (desarrollo)

npm test -- --watch

Con coverage

npm test -- --coverage

Integración con Jenkins

Los tests se ejecutan en el pipeline CI (Jenkinsfile.ci):

stage('Frontend: check & build') {
    steps {
        dir('frontend') {
            sh '''
                npm install
                npm run check
                npm test          # ← Ejecuta estos tests
                npm run build
            '''
        }
    }
}

Flujo en Jenkins

1. Push/MR → 2. Jenkins detecta → 3. npm test → 4. ¿Pasa? → 5. Build
                                        ↓ NO
                                   Pipeline FALLA
                                   (estudiante corrige)

Cómo Romper los Tests (Ejercicio)

Para ver Jenkins fallar, prueba:

Cambio Test que Falla
Cambiar prettify() para no capitalizar text.test.js
Cambiar endpoint de /menu a /menus api.test.js
Eliminar sección "Primeros" del HTML App.test.js

Glosario

Término Definición
Unit Test Test de una función/módulo aislado
Mock Simulación de una dependencia
Assertion Verificación de que algo es verdadero
Happy Path Escenario donde todo funciona bien
Edge Case Escenario límite o inusual
Coverage % del código ejecutado por tests