chore: excluir TESTS.md del repositorio
This commit is contained in:
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -6,3 +6,4 @@ dist
|
|||||||
*.log
|
*.log
|
||||||
.svelte-kit
|
.svelte-kit
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
|
TESTS.md
|
||||||
|
|||||||
@@ -1,273 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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()`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
npm install
|
|
||||||
npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### En modo watch (desarrollo)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm test -- --watch
|
|
||||||
```
|
|
||||||
|
|
||||||
### Con coverage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm test -- --coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Integración con Jenkins
|
|
||||||
|
|
||||||
Los tests se ejecutan en el pipeline CI (`Jenkinsfile.ci`):
|
|
||||||
|
|
||||||
```groovy
|
|
||||||
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 |
|
|
||||||
Reference in New Issue
Block a user