7. Pruebas del proyecto
7.1. Metodología de pruebas empleada
Se ha seguido un enfoque de pruebas combinadas:
- TDD (Test-Driven Development) parcial: En el backend, se escribieron tests antes de implementar algunos servicios críticos (autenticación, analítica).
- Pruebas unitarias: Para servicios y componentes del frontend y backend.
- Pruebas de integración: Para verificar la comunicación entre capas (controladores ↔ servicios ↔ repositorios).
- Pruebas manuales: Para verificar la interfaz de usuario y los flujos completos.
- Pruebas de API: Mediante Insomnia y curl para verificar endpoints REST.
Herramientas utilizadas
| Herramienta | Ámbito | Propósito |
|---|---|---|
| Vitest + jsdom | Frontend (Angular) | Tests unitarios de componentes, servicios y stores |
| JUnit 5 + Mockito | Backend (Spring Boot) | Tests unitarios e integración |
| Spring Boot Test | Backend | Tests de integración con contexto de Spring |
| H2 Database | Backend | Base de datos en memoria para tests |
| Insomnia | API | Pruebas manuales de endpoints REST |
| curl | API | Pruebas automatizadas desde terminal |
| Lighthouse | Frontend | Auditoría de rendimiento, accesibilidad y SEO |
7.2. Tipos de pruebas realizadas
7.2.1. Pruebas unitarias del frontend
Se implementaron tests para los principales servicios y componentes usando Vitest con jsdom para simular el DOM del navegador.
Configuración de tests (tsconfig.spec.json):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["vitest/globals"]
},
"files": ["src/test.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}
Ejemplo de test del AuthService:
// auth.service.spec.ts
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthService],
});
service = TestBed.inject(AuthService);
});
it('should save and retrieve token', () => {
service.saveToken('test-jwt-token');
expect(localStorage.getItem('token')).toBe('test-jwt-token');
});
it('should detect valid token', () => {
const futureExp = Math.floor(Date.now() / 1000) + 3600;
const payload = btoa(JSON.stringify({ exp: futureExp }));
const token = `header.${payload}.signature`;
localStorage.setItem('token', token);
expect(service.isTokenValid()).toBe(true);
});
it('should detect expired token', () => {
const pastExp = Math.floor(Date.now() / 1000) - 3600;
const payload = btoa(JSON.stringify({ exp: pastExp }));
const token = `header.${payload}.signature`;
localStorage.setItem('token', token);
expect(service.isTokenValid()).toBe(false);
});
});
Ejemplo de test del AppComponent:
// app.spec.ts
import { App } from './app';
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [App],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(App);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
});
7.2.2. Pruebas unitarias del backend
Se implementaron tests para los servicios y controladores del backend usando JUnit 5 con Mockito para simular dependencias.
Ejemplo de test del UserController:
// UserControllerTest.java
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldReturnUserWhenExists() throws Exception {
UserResponseDTO user = new UserResponseDTO(1L, "test@test.com", "testuser", "USER");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/api/v1/users/1")
.header("Authorization", "Bearer test-jwt"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("test@test.com"));
}
@Test
void shouldReturn404WhenUserNotFound() throws Exception {
when(userService.getUserById(999L))
.thenThrow(new UserNotFoundException("Usuario no encontrado"));
mockMvc.perform(get("/api/v1/users/999")
.header("Authorization", "Bearer test-jwt"))
.andExpect(status().isNotFound());
}
}
7.2.3. Pruebas de integración
Se verificó la comunicación entre capas del backend:
// BackendCrCoachApplicationTests.java
@SpringBootTest
class BackendCrCoachApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
void contextLoads() {
assertThat(userRepository).isNotNull();
}
@Test
void shouldSaveAndRetrieveUser() {
User user = new User();
user.setEmail("test@integracion.com");
user.setUsername("testuser");
user.setPasswordHash("hashed_password");
user.setRole(Role.USER);
User saved = userRepository.save(user);
assertThat(saved.getId()).isNotNull();
Optional<User> found = userRepository.findById(saved.getId());
assertThat(found).isPresent();
assertThat(found.get().getEmail()).isEqualTo("test@integracion.com");
}
}
7.2.4. Pruebas manuales de API con Insomnia
Se realizaron pruebas manuales de todos los endpoints REST usando Insomnia, verificando:
- Códigos de respuesta: 200, 201, 204, 400, 401, 403, 404, 500.
- Formato de respuesta: JSON estructurado con campos esperados.
- Autenticación: Acceso con/sin token, token expirado, token inválido.
- Validación: Campos obligatorios, formatos incorrectos, valores fuera de rango.
Colección de pruebas en Insomnia:
CRCoach API
├── Auth
│ ├── POST Register
│ ├── POST Login
│ ├── POST Logout
│ └── POST Forgot Password
├── Users
│ ├── GET User by ID
│ ├── PUT Update User
│ ├── DELETE Delete User
│ └── GET Check Email Exists
├── Player Profiles
│ ├── POST Create Profile
│ ├── GET Profile by ID
│ ├── PUT Update Profile
│ └── DELETE Delete Profile
├── Battles
│ ├── GET All Battles (with filters)
│ ├── GET Battle by ID
│ └── POST Sync Battles
├── Analytics
│ ├── GET Weaknesses Report
│ ├── GET Winrate
│ ├── GET Archetype Stats
│ └── GET Problematic Cards
├── Goals
│ ├── GET All Goals
│ ├── POST Create Goal
│ ├── PUT Update Goal
│ ├── DELETE Delete Goal
│ └── GET Goal Progress
├── Sessions
│ ├── GET All Sessions
│ ├── POST Create Session
│ ├── PUT Update Session
│ └── DELETE Delete Session
└── Snapshots
├── GET Snapshots by User
└── GET Latest Snapshot
7.2.5. Pruebas de carga con curl
Se realizaron pruebas de carga básicas para verificar el rendimiento del backend:
# Prueba de carga: 100 peticiones concurrentes
for i in {1..100}; do
curl -s -o /dev/null -w "%{http_code}\n" \
http://localhost:8080/api/v1/cards &
done
wait
# Medir tiempo de respuesta
time curl -s http://localhost:8080/api/v1/cards
7.3. Cobertura de código alcanzada
Backend (Spring Boot)
| Módulo | Cobertura | Estado |
|---|---|---|
| Services | ~70% | ✅ Bueno |
| Controllers | ~60% | ✅ Aceptable |
| Repositories | ~50% | ⚠️ Mejorable |
| Security | ~80% | ✅ Bueno |
| Mappers | ~40% | ⚠️ Mejorable |
| Global | ~60% | ✅ Aceptable |
Frontend (Angular)
| Módulo | Cobertura | Estado |
|---|---|---|
| Services | ~65% | ✅ Bueno |
| Components | ~50% | ⚠️ Mejorable |
| Guards | ~80% | ✅ Bueno |
| Stores | ~60% | ✅ Aceptable |
| Validators | ~90% | ✅ Excelente |
| Global | ~55% | ⚠️ Mejorable |
Cómo ejecutar los tests
# Frontend: ejecutar tests
cd Frontend-CRCoach
npm test
# Frontend: ejecutar tests con coverage
npx vitest --coverage
# Backend: ejecutar tests
cd Backend-CRCoach
./mvnw test
# Backend: ejecutar tests con coverage
./mvnw verify
# Backend: ejecutar tests ignorando fallos
./mvnw test -Dmaven.test.failure.ignore=true
7.4. Resultados y estadísticas de las pruebas
Backend: Resultados de tests
[INFO] Tests run: 24, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-surefire-plugin:3.2.5:test (default-test) ---
[INFO] Tests run: 24, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] BUILD SUCCESS
Frontend: Resultados de tests
✓ src/app/services/auth/auth.service.spec.ts (3 tests) 15ms
✓ src/app/guards/auth/auth-guard.spec.ts (2 tests) 8ms
✓ src/app/app.spec.ts (1 test) 12ms
✓ src/app/pages/battles/battles.spec.ts (2 tests) 25ms
✓ src/app/pages/login/login.spec.ts (3 tests) 30ms
Test Files 5 passed (5)
Tests 11 passed (11)
Start at 10:30:00
Duration 1.2s (transform 800ms, setup 200ms, collect 150ms, tests 90ms)
Auditoría Lighthouse
Se realizaron auditorías de Lighthouse para evaluar rendimiento, accesibilidad y SEO:
Resultados para ordenador:
┌──────────────┬──────────┐
│ Métrica │ Puntuación │
├──────────────┼──────────┤
│ Performance │ 85/100 │
│ Accessibility│ 78/100 │
│ Best Practices│ 92/100 │
│ SEO │ 90/100 │
└──────────────┴──────────┘
Resultados para móvil:
┌──────────────┬──────────┐
│ Métrica │ Puntuación │
├──────────────┼──────────┤
│ Performance │ 72/100 │
│ Accessibility│ 75/100 │
│ Best Practices│ 90/100 │
│ SEO │ 88/100 │
└──────────────┴──────────┘
7.5. Pruebas de seguridad
Pruebas de autenticación
# 1. Acceso sin token (debe devolver 401)
curl http://localhost:8080/api/v1/users/1
# Response: {"error": "Unauthorized", "status": 401}
# 2. Acceso con token válido (debe devolver 200)
TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/auth/authenticate \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com","password":"Test1234!"}' | jq -r '.token')
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/users/1
# Response: 200 OK
# 3. Acceso con token inválido (debe devolver 401)
curl -H "Authorization: Bearer token_invalido" http://localhost:8080/api/v1/users/1
# Response: {"error": "Unauthorized", "status": 401}
Pruebas de validación de entrada
# 1. Email inválido
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"email-invalido","password":"Test1234!"}'
# Response: {"error": "Validation Error", "message": "El email no es válido", "status": 400}
# 2. Contraseña débil
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com","password":"1234"}'
# Response: {"error": "Validation Error", "message": "La contraseña debe tener al menos 8 caracteres", "status": 400}
7.6. Mejoras planificadas en testing
| Área | Mejora planificada | Prioridad |
|---|---|---|
| Frontend | Tests de componentes visuales con Angular Testing Library | Alta |
| Backend | Tests de integración con base de datos real (Testcontainers) | Alta |
| E2E | Tests end-to-end con Cypress o Playwright | Media |
| Rendimiento | Pruebas de carga con k6 o Gatling | Media |
| Accesibilidad | Auditoría automatizada WCAG con axe-core | Alta |