Domine o test runner mais rápido do JavaScript, sem configuração
O Bun inclui um test runner nativo que elimina a necessidade de Jest, Vitest ou outras ferramentas de teste.
// src/utils/calculator.ts export function add(a: number, b: number): number { return a + b; } export function multiply(a: number, b: number): number { return a * b; } export function divide(a: number, b: number): number { if (b === 0) { throw new Error("Divisão por zero não é permitida"); } return a / b; } export function isEven(num: number): boolean { return num % 2 === 0; }
// src/utils/calculator.test.ts import { test, expect, describe } from "bun:test"; import { add, multiply, divide, isEven } from "./calculator"; describe("Calculator", () => { test("should add two numbers", () => { expect(add(2, 3)).toBe(5); expect(add(-1, 1)).toBe(0); expect(add(0, 0)).toBe(0); }); test("should multiply two numbers", () => { expect(multiply(3, 4)).toBe(12); expect(multiply(-2, 3)).toBe(-6); expect(multiply(0, 5)).toBe(0); }); test("should divide two numbers", () => { expect(divide(10, 2)).toBe(5); expect(divide(7, 2)).toBe(3.5); }); test("should throw error when dividing by zero", () => { expect(() => divide(5, 0)).toThrow("Divisão por zero não é permitida"); }); test("should check if number is even", () => { expect(isEven(4)).toBe(true); expect(isEven(3)).toBe(false); expect(isEven(0)).toBe(true); }); });
bun test
O Bun test runner é compatível com a API do Jest, incluindo todos os matchers principais:
expect(value).toBe(expected) expect(value).toEqual(expected) expect(value).toBeNull() expect(value).toBeUndefined() expect(value).toBeTruthy() expect(value).toBeFalsy() expect(value).toBeGreaterThan(3) expect(value).toBeCloseTo(0.3)
expect(array).toContain(item) expect(string).toMatch(/pattern/) expect(fn).toThrow() expect(fn).toThrow("message") expect(object).toHaveProperty("key") expect(array).toHaveLength(3)
// src/services/api.ts export async function fetchUser(id: number) { // Simula uma chamada de API await new Promise(resolve => setTimeout(resolve, 100)); if (id <= 0) { throw new Error("ID inválido"); } return { id, name: `User ${id}`, email: `user${id}@example.com` }; } export async function fetchUsers(): Promise<any[]> { await new Promise(resolve => setTimeout(resolve, 200)); return [ { id: 1, name: "João" }, { id: 2, name: "Maria" } ]; }
// src/services/api.test.ts import { test, expect, describe } from "bun:test"; import { fetchUser, fetchUsers } from "./api"; describe("API Service", () => { test("should fetch user by id", async () => { const user = await fetchUser(1); expect(user).toEqual({ id: 1, name: "User 1", email: "user1@example.com" }); }); test("should throw error for invalid id", async () => { await expect(fetchUser(-1)).rejects.toThrow("ID inválido"); }); test("should fetch all users", async () => { const users = await fetchUsers(); expect(users).toHaveLength(2); expect(users[0]).toHaveProperty("name", "João"); }); });
// src/services/database.test.ts import { test, expect, mock, spyOn } from "bun:test"; // Mock de uma função const mockFetch = mock(() => Promise.resolve({ json: () => Promise.resolve({ data: "test" }) })); test("should mock fetch", async () => { // @ts-ignore global.fetch = mockFetch; const response = await fetch("/api/test"); const data = await response.json(); expect(data).toEqual({ data: "test" }); expect(mockFetch).toHaveBeenCalledTimes(1); }); // Spy em métodos test("should spy on console.log", () => { const spy = spyOn(console, "log"); console.log("Hello, World!"); expect(spy).toHaveBeenCalledWith("Hello, World!"); spy.mockRestore(); });
# bunfig.toml [test] # Timeout padrão para testes (em ms) timeout = 5000 # Executar testes em paralelo parallel = true # Padrão de arquivos de teste testMatch = ["**/*.test.{js,ts,tsx,jsx}"] # Setup file (executado antes de cada teste) setupFiles = ["./test-setup.ts"]
// test-setup.ts import { beforeAll, afterAll } from "bun:test"; // Configuração global antes de todos os testes beforeAll(() => { console.log("🧪 Iniciando testes..."); // Configurar variáveis de ambiente para teste process.env.NODE_ENV = "test"; process.env.DATABASE_URL = "sqlite://test.db"; }); // Limpeza após todos os testes afterAll(() => { console.log("✅ Testes finalizados!"); });
bun test
- Executa todos os testesbun test --watch
- Modo watch (re-executa ao salvar)bun test calculator.test.ts
- Executa arquivo específicobun test --timeout 10000
- Define timeout personalizadobun test --bail
- Para no primeiro erroVamos criar testes para funções do nosso projeto Restaurantix:
validateEmail(email: string)
calculateOrderTotal(items: OrderItem[])
bun test --watch
// src/utils/validation.ts export function validateEmail(email: string): boolean { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } export interface OrderItem { name: string; price: number; quantity: number; } export function calculateOrderTotal(items: OrderItem[]): number { return items.reduce((total, item) => { return total + (item.price * item.quantity); }, 0); } // src/utils/validation.test.ts import { test, expect, describe } from "bun:test"; import { validateEmail, calculateOrderTotal } from "./validation"; describe("Validation Utils", () => { test("should validate email correctly", () => { expect(validateEmail("test@example.com")).toBe(true); expect(validateEmail("invalid-email")).toBe(false); expect(validateEmail("")).toBe(false); }); test("should calculate order total", () => { const items = [ { name: "Pizza", price: 25.90, quantity: 2 }, { name: "Refrigerante", price: 5.50, quantity: 1 } ]; expect(calculateOrderTotal(items)).toBe(57.30); expect(calculateOrderTotal([])).toBe(0); }); });