Nivel inicial11 min de lectura · Publicado el

Composición de un test en TDD y sus métodos, con ejemplos

Composición de un test como un examen: describe es el tema, it la pregunta y expect la respuesta; beforeEach reparte la hoja y afterEach la recoge. describe, it y expectrubentejera.com beforeEachafterEachPersongetAgeInYears(10)34describeitexpect
describe es el tema (Person); it, cada pregunta, como getAgeInYears(10); y expect, la respuesta correcta, 34. beforeEach reparte una hoja en blanco antes de cada pregunta y afterEach la recoge.

En TDD (desarrollo guiado por pruebas) escribes el test antes que el código. Para hacerlo con soltura necesitas conocer las piezas con las que se monta un test. Son pocas y se repiten en casi todas las herramientas de JavaScript: Jest, Jasmine, Vitest o el ejecutor de tests que trae Node.

Un fichero de tests se parece a un examen bien organizado: tiene temas (describe), preguntas (it) y la respuesta correcta de cada pregunta (expect). Antes de cada pregunta repartes una hoja en blanco (beforeEach) y al terminar la recoges (afterEach).

Si todavía no tienes Jest en tu proyecto, empieza por Instalar Jest en un proyecto de JavaScript.

Los métodos de un test, de un vistazo

Método Para qué sirve Cuándo se ejecuta
describe Agrupa tests relacionados, por ejemplo, los de una clase o un método Al cargar el fichero
it o test Define un caso de prueba concreto Una vez
expect Comprueba que un valor es el esperado, con un matcher Dentro de cada test
beforeEach Prepara lo que necesita cada test Antes de cada test de su describe
afterEach Limpia lo que haya dejado cada test Después de cada test de su describe
beforeAll Prepara algo costoso una sola vez Una vez, antes del primer test de su describe
afterAll Libera lo preparado con beforeAll Una vez, después del último test
Cómo se anidan las piezas del test de Person: describe('Person') contiene un beforeEach, que hace el Arrange con new Person('Ana', 'García', 24) antes de cada it, describe('getFullName') y describe('getAgeInYears'). Dentro de este, it('suma los años indicados a la edad actual') hace el Act, const age = person.getAgeInYears(10), y el Assert, expect(age).toBe(34). Los otros dos it reciben cada uno su persona nueva.
Así encajan las piezas en el test completo de Person que montamos más abajo: cada caja va dentro de la que la agrupa. El Arrange está en beforeEach, que se ejecuta antes de cada it; dentro del it quedan el Act y el Assert.

Vamos a montarlos uno a uno con una clase sencilla, Person, que guarda el nombre, el apellido y la edad de una persona:

// person.js
class Person {
  constructor(name, lastName, age) {
    this.name = name;
    this.lastName = lastName;
    this.age = age;
  }

  getFullName() {
    return `${this.name} ${this.lastName}`;
  }

  // Edad que tendrá la persona dentro de `years` años
  getAgeInYears(years) {
    if (years < 0) throw new RangeError('Los años no pueden ser negativos');
    return this.age + years;
  }
}

module.exports = { Person };

describe: agrupar los tests

describe recibe un nombre y una función con los tests del grupo. Se pueden anidar: un describe para la clase y, dentro, uno por cada método.

describe('Person', () => {
  describe('getFullName', () => {
    // tests de getFullName
  });

  describe('getAgeInYears', () => {
    // tests de getAgeInYears
  });
});

Así, cuando algo falla, el informe te dice exactamente dónde: Person › getAgeInYears › ….

it (o test): un caso de prueba

Cada it comprueba un comportamiento concreto. En Jest, it y test son lo mismo: it es un alias de test. Usa el que se lea mejor; it suele quedar natural cuando el nombre describe lo que hace el código.

describe('getAgeInYears', () => {
  it('suma los años indicados a la edad actual', () => {
    // …
  });

  it('lanza un error si los años son negativos', () => {
    // …
  });
});

El nombre del test es documentación: debe decir qué se espera, no cómo se comprueba. Si lees solo los nombres, deberías entender qué hace la clase.

expect: comprobar el resultado

expect recibe el valor que ha producido tu código y lo compara con el esperado mediante un matcher, un método que hace la comprobación. Estos son los más usados en Jest:

expect(6 + 6).toBe(12);                                  // igualdad exacta (Object.is)
expect({ name: 'Ana' }).toEqual({ name: 'Ana' });        // mismo contenido, aunque sean objetos distintos
expect(1 === 1).toBeTruthy();                            // cualquier valor «verdadero»
expect(null).toBeNull();
expect(['a', 'b']).toContain('b');
expect(0.1 + 0.2).toBeCloseTo(0.3);                      // decimales con margen de redondeo
expect(6 + 6).not.toBe(13);                              // .not invierte cualquier matcher
expect(() => person.getAgeInYears(-10)).toThrow(RangeError);

Jasmine usa casi los mismos nombres (toBe, toEqual, toBeTruthy, toBeNull…). Tienes la lista completa en la documentación de matchers de Jasmine y en la de expect de Jest.

beforeEach y afterEach: preparar y recoger

Si todos los tests de un grupo necesitan lo mismo, no lo repitas en cada uno: créalo en beforeEach. Se ejecuta antes de cada it del describe donde está, y de los describe que haya dentro.

describe('Person', () => {
  let person;

  beforeEach(() => {
    person = new Person('Ana', 'García', 24); // un objeto nuevo para cada test
  });

  // todos los tests de aquí dentro reciben su propia persona recién creada
});

afterEach hace lo contrario: se ejecuta después de cada test. Sirve para deshacer lo que el test haya dejado a medias, como cerrar una conexión, borrar un fichero temporal o restaurar un mock, para que el siguiente test empiece limpio.

Volviendo al examen: cada alumno recibe una hoja en blanco, no la del compañero de delante. Si un test dependiera de lo que dejó el anterior, cambiar el orden lo rompería, y encontrar el fallo costaría mucho.

beforeAll y afterAll: una sola vez

Cuando preparar algo es lento (arrancar una base de datos de pruebas, por ejemplo), beforeAll lo hace una vez para todo el grupo y afterAll lo libera al final. Este fichero registra el orden en que se ejecuta cada pieza:

const log = [];

describe('orden de los hooks', () => {
  beforeAll(() => { log.push('beforeAll'); });
  beforeEach(() => { log.push('beforeEach'); });
  afterEach(() => { log.push('afterEach'); });
  afterAll(() => { console.log([...log, 'afterAll'].join(' → ')); });

  it('primero', () => { log.push('test 1'); });
  it('segundo', () => { log.push('test 2'); });
});

Al ejecutarlo con Jest 30.5, imprime:

beforeAll → beforeEach → test 1 → afterEach → beforeEach → test 2 → afterEach → afterAll

Úsalos con cuidado: lo que crea beforeAll lo comparten todos los tests, y si uno lo modifica, afecta a los demás.

Dentro de cada test: Arrange, Act, Assert

Ya sabes cómo se organiza el fichero. Falta ordenar el interior de cada test. El patrón más extendido son tres pasos, conocidos como Arrange, Act, Assert (AAA):

  1. Arrange (preparar): crea los datos y objetos que necesitas.
  2. Act (actuar): ejecuta lo que quieres probar, normalmente una sola llamada.
  3. Assert (comprobar): verifica el resultado con expect.
it('suma los años indicados a la edad actual', () => {
  const person = new Person('Ana', 'García', 24);     // Arrange

  const age = person.getAgeInYears(10);               // Act

  expect(age).toBe(34);                               // Assert
});

Separar los tres bloques con una línea en blanco basta para que se lean de un vistazo. Cuando el Arrange se repite en varios tests, es la señal para moverlo a beforeEach.

Un test, un comportamiento.

El test completo

Juntando todas las piezas:

// person.test.js
const { Person } = require('./person');

describe('Person', () => {
  let person;

  beforeEach(() => {
    person = new Person('Ana', 'García', 24);
  });

  describe('getFullName', () => {
    it('devuelve el nombre y el apellido separados por un espacio', () => {
      const fullName = person.getFullName();

      expect(fullName).toBe('Ana García');
    });
  });

  describe('getAgeInYears', () => {
    it('suma los años indicados a la edad actual', () => {
      const age = person.getAgeInYears(10);

      expect(age).toBe(34);
    });

    it('con 0 años devuelve la edad actual', () => {
      expect(person.getAgeInYears(0)).toBe(24);
    });

    it('lanza un error si los años son negativos', () => {
      expect(() => person.getAgeInYears(-10)).toThrow(RangeError);
    });
  });
});

Ejecútalo con npx jest --verbose (Jest 30.5.2, Node 24.21.0) y verás cada test en su grupo:

PASS ./person.test.js
  Person
    getFullName
      ✓ devuelve el nombre y el apellido separados por un espacio (2 ms)
    getAgeInYears
      ✓ suma los años indicados a la edad actual
      ✓ con 0 años devuelve la edad actual (1 ms)
      ✓ lanza un error si los años son negativos (16 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        0.27 s, estimated 1 s
Ran all test suites.

Si un test falla, Jest te enseña qué esperaba y qué recibió:

● Person › getAgeInYears › suma los años indicados a la edad actual

  expect(received).toBe(expected) // Object.is equality

  Expected: 35
  Received: 34

Lo mismo sin instalar nada: node:test

Desde Node 20, Node trae su propio ejecutor de tests, estable y sin dependencias. Usa los mismos nombres (describe, it, beforeEach, afterEach), pero las comprobaciones se hacen con el módulo assert en lugar de expect:

// person.test.js (con "type": "module" en package.json)
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { Person } from './person.js';

describe('Person', () => {
  let person;

  beforeEach(() => {
    person = new Person('Ana', 'García', 24);
  });

  it('getFullName devuelve el nombre y el apellido', () => {
    assert.equal(person.getFullName(), 'Ana García');
  });

  it('getAgeInYears suma los años a la edad actual', () => {
    assert.equal(person.getAgeInYears(10), 34);
  });

  it('getAgeInYears lanza un error si los años son negativos', () => {
    assert.throws(() => person.getAgeInYears(-10), RangeError);
  });
});

Se ejecuta con node --test. Aquí person.js usa export class Person en lugar de module.exports. Los equivalentes de beforeAll y afterAll se llaman before y after.

Jest node:test
expect(a).toBe(b) assert.equal(a, b) (con node:assert/strict)
expect(a).toEqual(b) assert.deepEqual(a, b)
expect(fn).toThrow(Error) assert.throws(fn, Error)
beforeAll / afterAll before / after

Cuando los tests los escribe una IA

Un agente de IA escribe tests muy rápido, y precisamente por eso conviene revisarlos con las mismas reglas de este artículo. Desde el enfoque de IA Limpia, estos son los criterios que proponemos:

  1. Lee solo los nombres de los it: ¿describen el comportamiento que querías? Si no, el test no documenta nada.
  2. Comprueba que cada test sigue Arrange, Act, Assert y prueba una sola cosa.
  3. Si un test falla, que la IA arregle el código, no el expect. Cambiar lo esperado para que pase en verde es borrar la alarma, no apagar el fuego.

Un test bien compuesto también puede ser frágil si comprueba cómo está hecho el código en lugar de lo que hace. Las señales para detectarlo están en tests frágiles.

Siguiente paso

Coge una función pequeña de tu proyecto y escríbele tres tests con esta estructura: un caso normal, un caso límite (como el 0) y un caso de error. Si quieres ver cómo encajan en el ciclo completo de TDD, sigue con TDD: desarrollo dirigido por test.

Fuentes

Comprobado en noviembre de 2026:

  • Jest 30.5: API global (describe, test y su alias it, beforeEach, afterEach, beforeAll, afterAll) y expect (toBe con Object.is, toEqual, toThrow, toBeCloseTo, .not).
  • Node.js v26: Test runner (node:test estable desde Node 20; describe es alias de suite e it de test).
  • Jasmine: matchers.
  • Arrange, Act, Assert: Bill Wake, «3A – Arrange, Act, Assert» (2011), donde cuenta que observó y dio nombre al patrón en 2001.

Todo el código del artículo se ha ejecutado con Node 24.21.0: los ejemplos de Jest con Jest 30.5.2 y el de node:test con node --test.