Testing con Vitest y calidad con Biome

Avanzado
TypeScript
TypeScript
Actualizado: 18/04/2026

Vitest como test runner moderno

Vitest es un test runner construido sobre Vite que ejecuta directamente TypeScript y ESM sin paso de compilación previo. Hereda la configuración de Vite si existe, lo que permite reutilizar alias, plugins y transformadores del mismo pipeline que sirve la aplicación.

La instalación es mínima:

npm install -D vitest

El comando vitest arranca en watch mode por defecto y recorre ficheros con extensiones *.test.ts o *.spec.ts. Para una sola pasada se usa vitest run.

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

La API de Vitest replica deliberadamente la de Jest: describe, it, expect y los hooks beforeEach, afterEach, beforeAll y afterAll tienen el mismo comportamiento, facilitando la migración.

Estructura de un test con Vitest

Un test describe un escenario, ejecuta código y comprueba el resultado con un matcher. Los helpers clave se importan desde vitest.

// src/carrito.ts
export type Articulo = { id: string; precio: number; cantidad: number }

export function totalCarrito(articulos: Articulo[]): number {
    return articulos.reduce((suma, a) => suma + a.precio * a.cantidad, 0)
}
// src/carrito.test.ts
import { describe, it, expect } from "vitest"
import { totalCarrito } from "./carrito"

describe("totalCarrito", () => {
    it("suma correctamente precios por cantidad", () => {
        const resultado = totalCarrito([
            { id: "a", precio: 10, cantidad: 2 },
            { id: "b", precio: 5, cantidad: 3 }
        ])
        expect(resultado).toBe(35)
    })

    it("devuelve 0 con carrito vacio", () => {
        expect(totalCarrito([])).toBe(0)
    })
})

El watch mode ejecuta únicamente los tests afectados por el último cambio, lo que reduce drásticamente la latencia en proyectos grandes.

Hooks y fixtures tipadas

Vitest permite compartir estado entre tests con hooks y, desde versiones recientes, con test.extend para definir fixtures tipadas al estilo Playwright.

import { beforeEach, describe, expect, test as base } from "vitest"

type Contexto = {
    carrito: Articulo[]
}

const test = base.extend<Contexto>({
    carrito: async ({}, use) => {
        const items: Articulo[] = [
            { id: "a", precio: 10, cantidad: 1 }
        ]
        await use(items)
    }
})

describe("carrito", () => {
    test("empieza con un articulo", ({ carrito }) => {
        expect(carrito).toHaveLength(1)
    })
})

Mocking con vi

El objeto vi agrupa utilidades de mocking. Las más usadas son vi.fn para crear funciones espía y vi.mock para sustituir un módulo entero.

import { describe, it, expect, vi } from "vitest"

vi.mock("./correo", () => ({
    enviar: vi.fn().mockResolvedValue({ ok: true })
}))

import { enviar } from "./correo"
import { registrarUsuario } from "./registro"

describe("registrarUsuario", () => {
    it("envia correo de bienvenida", async () => {
        await registrarUsuario({ email: "u@ejemplo.com" })
        expect(enviar).toHaveBeenCalledWith(expect.objectContaining({
            destino: "u@ejemplo.com"
        }))
    })
})

La cobertura se activa con la bandera --coverage y por defecto usa el motor v8, integrado en Node.js. Los informes se generan en coverage/ con formato HTML navegable.

Biome como linter y formateador unificado

Biome es una cadena de herramientas escrita en Rust que reemplaza a ESLint y Prettier con un único binario. Ofrece un formateador opinado, un linter con reglas para JavaScript, TypeScript, JSX y JSON, y un modo de organización automática de imports.

npm install -D --save-exact @biomejs/biome
npx biome init

El comando init genera un biome.json con la configuración por defecto:

{
    "$schema": "https://biomejs.dev/schemas/latest/schema.json",
    "files": {
        "includes": ["**/*.ts", "**/*.tsx", "**/*.json"]
    },
    "formatter": {
        "enabled": true,
        "indentStyle": "space",
        "indentWidth": 4,
        "lineWidth": 100
    },
    "linter": {
        "enabled": true,
        "rules": {
            "recommended": true
        }
    },
    "assist": {
        "actions": {
            "source": {
                "organizeImports": "on"
            }
        }
    }
}

El binario expone comandos concisos para cada fase del ciclo:

npx biome format --write .
npx biome lint .
npx biome check --write .

El subcomando check combina lint, formato y organización de imports en una sola pasada, y es la orden recomendada para hooks de pre-commit y pipelines de integración continua.

Ventajas frente a ESLint + Prettier

La propuesta de Biome no es aportar más reglas, sino aportar la misma función con otras propiedades operativas:

  • Ejecución en Rust, varios órdenes de magnitud más rápida que cadenas basadas en Node.js
  • Configuración con un único fichero, sin conflictos entre linter y formateador
  • Instalación con una sola dependencia, sin gestión de plugins
  • Compatibilidad creciente con reglas equivalentes a typescript-eslint

Biome cubre el 80 por ciento de los casos del día a día. Proyectos con reglas muy específicas de ESLint pueden necesitar complementarlo. Muchos equipos ejecutan Biome para formato y lint básico y reservan ESLint para reglas personalizadas o para las heurísticas type-aware más elaboradas.

Oxlint como alternativa enfocada en linting

Oxlint es otro linter en Rust, más reciente, centrado exclusivamente en análisis estático. Se integra cómodamente junto a Prettier o junto a Biome en modo solo formateador. Oxlint se posiciona como un acelerador del flujo actual: ejecuta miles de reglas en milisegundos y no requiere migración de configuración. Los proyectos lo adoptan como primer filtro y dejan las reglas complejas de ESLint para una segunda pasada.

Integración en un proyecto TypeScript

Un proyecto típico combina Vitest para pruebas y Biome para calidad:

{
    "scripts": {
        "dev": "vite",
        "build": "tsc -b && vite build",
        "test": "vitest",
        "test:run": "vitest run",
        "lint": "biome check --write .",
        "format": "biome format --write ."
    }
}

El bucle de desarrollo queda corto: al guardar un fichero, el IDE con LSP de TypeScript avisa de errores de tipo, Vitest ejecuta los tests afectados y Biome formatea y corrige lo corregible. La cadena clásica de Jest más ESLint más Prettier se reduce a dos herramientas con configuración mínima.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de TypeScript

Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Configurar Vitest para proyectos TypeScript sin compilación previa. Escribir tests unitarios con expect, describe y fixtures. Aplicar mocking con vi.fn y vi.mock. Configurar Biome como alternativa unificada a ESLint y Prettier. Entender las ventajas en velocidad y ergonomía respecto a las cadenas clásicas.