TL;DR

  • Mocha provee estructura de tests (describe, it) — trae tu propia librería de assertions
  • Combina con Chai para assertions legibles: expect(value).to.equal(expected)
  • Hooks: before, after, beforeEach, afterEach para setup/teardown
  • Soporte async: callbacks (done), promises, async/await — todo funciona
  • Flexible y modular — elige tus herramientas

Ideal para: Proyectos Node.js, equipos que quieren elegir librería de assertions Omite si: Prefieres soluciones todo-en-uno (usa Jest) Tiempo de lectura: 14 minutos

Tu proyecto Node.js necesita tests. Jest se siente pesado para un servicio pequeño. Quieres elegir tu propio estilo de assertions. Necesitas algo que funcione con tus herramientas existentes.

Mocha no estorba. Ejecuta tests, provee hooks, maneja async — nada más. Tú eliges la librería de assertions, herramienta de mocking y reporter.

¿Qué es Mocha?

Mocha es un framework de testing JavaScript para Node.js y navegadores. Provee estructura para organizar tests pero intencionalmente deja assertions y mocking a otras librerías.

Por qué Mocha:

  • Flexible — funciona con cualquier librería de assertions
  • Async-friendly — callbacks, promises, async/await
  • Ecosistema rico — muchos reporters y plugins
  • Soporte browser — mismos tests corren en Node y navegador

Instalación y Setup

npm install mocha chai --save-dev
// package.json
{
    "scripts": {
        "test": "mocha",
        "test:watch": "mocha --watch"
    }
}
// .mocharc.json
{
    "spec": "test/**/*.test.js",
    "timeout": 5000,
    "recursive": true
}

Escribiendo Tu Primer Test

// test/calculator.test.js
const { expect } = require('chai');
const Calculator = require('../src/calculator');

describe('Calculator', () => {
    describe('add()', () => {
        it('debería sumar dos números positivos', () => {
            const calc = new Calculator();
            expect(calc.add(2, 3)).to.equal(5);
        });

        it('debería manejar números negativos', () => {
            const calc = new Calculator();
            expect(calc.add(-1, 1)).to.equal(0);
        });
    });
});

Chai Assertions

const { expect } = require('chai');

// Igualdad
expect(value).to.equal(5);
expect(value).to.deep.equal({ a: 1 });

// Verificación de tipos
expect('hello').to.be.a('string');
expect([1, 2]).to.be.an('array');

// Comparaciones
expect(10).to.be.above(5);
expect(10).to.be.below(20);

// Strings
expect('hello world').to.include('world');
expect('hello').to.have.lengthOf(5);

// Arrays
expect([1, 2, 3]).to.include(2);
expect([1, 2, 3]).to.have.lengthOf(3);

// Objetos
expect({ a: 1 }).to.have.property('a');

// Errores
expect(() => fn()).to.throw();
expect(() => fn()).to.throw('mensaje de error');

Lifecycle Hooks

describe('User Service', () => {
    let db;

    before(async () => {
        // Una vez antes de todos los tests
        db = await connectToDatabase();
    });

    after(async () => {
        // Una vez después de todos los tests
        await db.close();
    });

    beforeEach(() => {
        // Antes de cada test
    });

    afterEach(async () => {
        // Después de cada test
        await db.collection('users').deleteMany({});
    });

    it('debería crear usuario', async () => {
        // ...
    });
});

Testing Async

Async/Await (Recomendado)

it('debería obtener usuario', async () => {
    const user = await fetchUser(1);
    expect(user.name).to.equal('John');
});

it('debería manejar errores', async () => {
    try {
        await fetchUser(999);
        expect.fail('Debería haber lanzado error');
    } catch (err) {
        expect(err.message).to.include('not found');
    }
});

Timeout

it('debería completar en 5 segundos', async function() {
    this.timeout(5000);
    await longRunningOperation();
});

Mocking con Sinon

npm install sinon --save-dev
const sinon = require('sinon');
const axios = require('axios');

describe('API Client', () => {
    let axiosStub;

    beforeEach(() => {
        axiosStub = sinon.stub(axios, 'get');
    });

    afterEach(() => {
        axiosStub.restore();
    });

    it('debería obtener datos', async () => {
        axiosStub.resolves({ data: { id: 1, name: 'Test' } });

        const result = await apiClient.fetchUser(1);

        expect(result.name).to.equal('Test');
    });
});

Integración CI/CD

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

Mocha con Asistencia de IA

Lo que la IA hace bien:

  • Generar casos de prueba desde firmas de funciones
  • Crear variaciones de assertions para edge cases
  • Convertir entre estilos de assertions
  • Sugerir implementaciones de mock

Lo que necesita humanos:

  • Decidir qué testear
  • Entender lógica de negocio
  • Depurar tests flaky

FAQ

¿Qué es Mocha?

Mocha es un framework de testing JavaScript flexible para Node.js y navegadores. Provee estructura de tests (describe, it), lifecycle hooks (before, after) y soporte async, pero intencionalmente no incluye assertions o mocking. Lo combinas con Chai para assertions y Sinon para mocking.

¿Mocha vs Jest — cuál es mejor?

Jest es todo-en-uno con assertions, mocking y coverage integrados. Mocha es modular — eliges cada componente. Jest es más simple para empezar; Mocha ofrece más flexibilidad. Usa Jest para proyectos React, Mocha cuando quieras estilos específicos de assertions.

¿Qué librería de assertions funciona con Mocha?

Chai es la opción más popular con tres estilos: expect (BDD), should (BDD) y assert (TDD). El módulo assert integrado de Node también funciona. La mayoría de equipos prefieren el estilo expect de Chai por legibilidad.

¿Puede Mocha testear código async?

Sí, excelentemente. Mocha soporta tres patrones async: callbacks (parámetro done), promises (retornar la promise) y async/await (solo usar función async). Mocha maneja todos los patrones nativamente.

Recursos Oficiales

Ver También