backend-test

SKILL.md

Backend Test Skill

Escribe tests unitarios e integracion para el backend Express + Prisma.

When to Activate

  • Anadir tests a un servicio o controller existente
  • Crear tests para un nuevo modulo
  • Validar integraciones con APIs externas (mock)
  • Verificar autenticacion y autorizacion en endpoints
  • Mejorar cobertura de tests

Setup

Instalacion

cd ocralis_backend
npm install -D vitest supertest @vitest/coverage-v8

vitest.config.js

import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "node",
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
      include: ["services/**", "controllers/**", "utils/**"],
    },
  },
});

Script en package.json

{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage"
  }
}

Patrones de Test

Test Unitario de Servicio

// tests/services/geocoding.service.test.js
import { describe, it, expect, vi } from "vitest";
import { getCoordinates } from "../../services/geocoding.service.js";

describe("geocoding.service", () => {
  it("deberia devolver coordenadas para una direccion valida", async () => {
    const result = await getCoordinates({
      street: "Gran Via 1",
      postalcode: "28013",
      state: "Madrid",
      country: "Spain",
    });

    expect(result).toHaveProperty("latitude");
    expect(result).toHaveProperty("longitude");
    expect(typeof result.latitude).toBe("number");
  });

  it("deberia devolver null para direccion invalida", async () => {
    const result = await getCoordinates({ street: "xxxxxxxxxxx" });
    expect(result).toBeNull();
  });
});

Test de Integracion de Endpoint

// tests/routes/auth.routes.test.js
import { describe, it, expect } from "vitest";
import request from "supertest";
import app from "../../app.js";

describe("POST /api/auth/login", () => {
  it("deberia rechazar credenciales invalidas", async () => {
    const res = await request(app)
      .post("/api/auth/login")
      .send({ email: "noexiste@test.com", password: "wrongpass" });

    expect(res.status).toBe(401);
    expect(res.body.success).toBe(false);
  });

  it("deberia devolver tokens con credenciales validas", async () => {
    const res = await request(app)
      .post("/api/auth/login")
      .send({ email: "test@test.com", password: "password123" });

    expect(res.status).toBe(200);
    expect(res.body).toHaveProperty("accessToken");
    expect(res.body).toHaveProperty("refreshToken");
  });
});

Test con Endpoint Protegido

// tests/routes/employee.routes.test.js
import { describe, it, expect, beforeAll } from "vitest";
import request from "supertest";
import app from "../../app.js";

describe("GET /api/employees", () => {
  let token;

  beforeAll(async () => {
    const res = await request(app)
      .post("/api/auth/login")
      .send({ email: "test@test.com", password: "password123" });
    token = res.body.accessToken;
  });

  it("deberia rechazar sin token", async () => {
    const res = await request(app).get("/api/employees");
    expect(res.status).toBe(401);
  });

  it("deberia devolver lista con token valido", async () => {
    const res = await request(app)
      .get("/api/employees")
      .set("Authorization", `Bearer ${token}`);
    expect(res.status).toBe(200);
    expect(res.body.success).toBe(true);
    expect(Array.isArray(res.body.data)).toBe(true);
  });
});

Mock de Prisma

import { vi } from "vitest";

vi.mock("../../prismaClient.js", () => ({
  prisma: {
    usuario: {
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
    },
    employee: {
      findMany: vi.fn(),
      findUnique: vi.fn(),
      create: vi.fn(),
    },
    company: {
      findMany: vi.fn(),
      findUnique: vi.fn(),
    },
    // ... otros modelos segun necesidad
  },
}));

Mock de Servicio Externo

import { vi } from "vitest";

vi.mock("../../services/saltra.service.js", () => ({
  darAlta: vi.fn().mockResolvedValue({ success: true, naf: "123456789012" }),
  darBaja: vi.fn().mockResolvedValue({ success: true }),
  consultarNSS: vi.fn().mockResolvedValue({ nss: "123456789012" }),
}));

Estructura de Tests

ocralis_backend/tests/
├── services/
│   ├── geocoding.service.test.js
│   ├── saltra.service.test.js
│   ├── ai.service.test.js
│   ├── storage.service.test.js
│   └── documentType.service.test.js
├── controllers/
│   ├── auth.controller.test.js
│   ├── employee.controller.test.js
│   └── company.controller.test.js
├── routes/
│   ├── auth.routes.test.js
│   ├── employee.routes.test.js
│   └── company.routes.test.js
└── utils/
    ├── distance.test.js
    └── token.test.js

Convenciones

Concepto Convencion
Framework Vitest (compatible con ES Modules)
HTTP testing Supertest con la app Express (import app from "../../app.js")
Mocks vi.mock() para Prisma y servicios externos
Naming {archivo}.test.js
Descripciones En espanol
Cobertura Objetivo minimo 60% para servicios criticos
Estructura tests/{capa}/{archivo}.test.js

Checklist

  • vitest.config.js creado
  • Scripts de test en package.json
  • Tests unitarios para servicios criticos
  • Tests de integracion para endpoints principales
  • Mocks de Prisma y servicios externos
  • Tests de autenticacion (con y sin token)
  • Tests de validacion (datos invalidos)
  • Cobertura >= 60%
Weekly Installs
1
First Seen
6 days ago
Installed on
amp1
cline1
trae1
trae-cn1
opencode1
cursor1