Testing en Clean Architecture

La arquitectura limpia facilita testing porque separa comportamiento de detalles.

La pregunta clave es: ¿qué quiero demostrar en esta capa?

Dominio

Prueba reglas puras.

No debería haber mocks, base de datos, red ni framework.

it("marks an invoice as overdue after its due date", () => {
  const invoice = new Invoice({
    dueDate: new Date("2026-01-10"),
    paidAt: null,
  });
 
  expect(invoice.isOverdue(new Date("2026-01-11"))).toBe(true);
});

Application

Prueba casos de uso.

Aquí interesa verificar comportamiento completo de una operación, pero sustituyendo los límites externos por fakes o repositorios in-memory.

it("creates a task in the requested project", async () => {
  const tasks = new InMemoryTaskRepository();
  const projects = new InMemoryProjectRepository([
    { id: "project-1", name: "Website" },
  ]);
 
  const createTask = createTaskUseCase({ tasks, projects });
 
  await createTask({
    projectId: "project-1",
    title: "Prepare deploy",
  });
 
  expect(await tasks.findByProjectId("project-1")).toHaveLength(1);
});

Interface Adapters

Prueba validación, mapping y formato de salida.

No hace falta volver a probar toda la lógica del caso de uso. Para eso ya están los tests de application.

it("returns a validation error when title is empty", async () => {
  const result = await createTaskController({
    body: { title: "" },
  });
 
  expect(result.status).toBe(400);
});

Infrastructure

Prueba adapters reales con tests de integración.

Ejemplos:

  • Un repositorio guarda y recupera correctamente.
  • Un cliente HTTP interpreta errores externos.
  • Un adapter de filesystem maneja rutas inexistentes.

Estos tests pueden necesitar base de datos de test, contenedores o fixtures.

Presentation

Prueba solo lo que pertenece a la UI:

  • Estados visibles.
  • Interacción del usuario.
  • Mensajes de error.
  • Navegación.
  • Flujos críticos.

La UI no debería ser el único sitio donde se prueban reglas de negocio.

Estrategia recomendada

  • Muchos tests unitarios de dominio y casos de uso.
  • Algunos tests de integración para adapters importantes.
  • Pocos E2E para flujos críticos.
  • Tests de regresión para bugs reales.

Señales de mal diseño

  • Para testear una regla de negocio hay que renderizar React.
  • Para probar un caso de uso hay que conectar una base de datos real.
  • Los tests fallan si cambia el texto interno de una implementación.
  • Casi todos los tests verifican llamadas a mocks.
  • La lógica está repartida entre controllers, componentes y repositorios.

Relacionado