Skip to content

Defina testes com uma API semelhante ao Jest importada do módulo integrado bun:test. A longo prazo, o Bun visa compatibilidade completa com Jest; no momento, um conjunto limitado de matchers expect é suportado.

Uso Básico ​

Para definir um teste simples:

math.test.ts
ts
import { expect, test } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});

Agrupando Testes ​

Testes podem ser agrupados em suítes com describe.

math.test.ts
ts
import { expect, test, describe } from "bun:test";

describe("aritmética", () => {
  test("2 + 2", () => {
    expect(2 + 2).toBe(4);
  });

  test("2 * 2", () => {
    expect(2 * 2).toBe(4);
  });
});

Testes Assíncronos ​

Testes podem ser assíncronos.

math.test.ts
ts
import { expect, test } from "bun:test";

test("2 * 2", async () => {
  const result = await Promise.resolve(2 * 2);
  expect(result).toEqual(4);
});

Alternativamente, use o callback done para sinalizar conclusão. Se você incluir o callback done como parâmetro na definição do seu teste, deve chamá-lo ou o teste ficará pendurado.

math.test.ts
ts
import { expect, test } from "bun:test";

test("2 * 2", done => {
  Promise.resolve(2 * 2).then(result => {
    expect(result).toEqual(4);
    done();
  });
});

Timeouts ​

Opcionalmente especifique um timeout por teste em milissegundos passando um número como terceiro argumento para test.

math.test.ts
ts
import { test } from "bun:test";

test("wat", async () => {
  const data = await slowOperation();
  expect(data).toBe(42);
}, 500); // teste deve rodar em <500ms

Em bun:test, timeouts de teste lançam uma exceção não capturável para forçar o teste a parar e falhar. Também matamos quaisquer processos filhos que foram spawnados no teste para evitar deixar processos zumbis vagando no background.

O timeout padrão para cada teste é 5000ms (5 segundos) se não for substituído por esta opção de timeout ou jest.setDefaultTimeout().

Retries e Repeats ​

test.retry ​

Use a opção retry para tentar novamente automaticamente um teste se falhar. O teste passa se for bem-sucedido dentro do número especificado de tentativas. Isso é útil para testes instáveis que podem falhar intermitentemente.

example.test.ts
ts
import { test } from "bun:test";

test(
  "requisição de rede instável",
  async () => {
    const response = await fetch("https://example.com/api");
    expect(response.ok).toBe(true);
  },
  { retry: 3 }, // Tentar novamente até 3 vezes se o teste falhar
);

test.repeats ​

Use a opção repeats para executar um teste múltiplas vezes independentemente do status de passar/falhar. O teste falha se qualquer iteração falhar. Isso é útil para detectar testes instáveis ou teste de estresse. Note que repeats: N executa o teste N+1 vezes no total (1 execução inicial + N repetições).

example.test.ts
ts
import { test } from "bun:test";

test(
  "garantir que teste é estável",
  () => {
    expect(Math.random()).toBeLessThan(1);
  },
  { repeats: 20 }, // Executa 21 vezes no total (1 inicial + 20 repetições)
);

NOTE

Você não pode usar tanto `retry` quanto `repeats` no mesmo teste.

🧟 Zombie Process Killer ​

Quando um teste atinge o timeout e processos spawnados no teste via Bun.spawn, Bun.spawnSync ou node:child_process não são mortos, eles serão automaticamente mortos e uma mensagem será registrada no console. Isso previne que processos zumbis fiquem vagando no background após testes com timeout.

Modificadores de Teste ​

test.skip ​

Ignore testes individuais com test.skip. Estes testes não serão executados.

math.test.ts
ts
import { expect, test } from "bun:test";

test.skip("wat", () => {
  // TODO: corrigir isso
  expect(0.1 + 0.2).toEqual(0.3);
});

test.todo ​

Marque um teste como todo com test.todo. Estes testes não serão executados.

math.test.ts
ts
import { expect, test } from "bun:test";

test.todo("corrigir isso", () => {
  myTestFunction();
});

Para executar testes todo e encontrar quaisquer que estão passando, use bun test --todo.

bash
bun test --todo
my.test.ts:
✗ funcionalidade não implementada
  ^ este teste é marcado como todo mas passa. Remova `.todo` ou verifique se teste está correto.

 0 aprovações
 1 falha
 1 chamada expect()

Com esta flag, testes todo com falha não causarão erro, mas testes todo que passam serão marcados como falhando para que você possa remover a marca todo ou corrigir o teste.

test.only ​

Para executar um teste ou suíte de testes específica use test.only() ou describe.only().

example.test.ts
ts
import { test, describe } from "bun:test";

test("teste #1", () => {
  // não executa
});

test.only("teste #2", () => {
  // executa
});

describe.only("only", () => {
  test("teste #3", () => {
    // executa
  });
});

O seguinte comando executará apenas testes #2 e #3.

bash
bun test --only

O seguinte comando executará apenas testes #1, #2 e #3.

bash
bun test

test.if ​

Para executar um teste condicionalmente, use test.if(). O teste será executado se a condição for verdadeira. Isso é particularmente útil para testes que devem ser executados apenas em arquiteturas ou sistemas operacionais específicos.

example.test.ts
ts
test.if(Math.random() > 0.5)("executa metade das vezes", () => {
  // ...
});

const macOS = process.platform === "darwin";
test.if(macOS)("executa no macOS", () => {
  // executa se macOS
});

test.skipIf ​

Para ignorar um teste baseado em alguma condição, use test.skipIf() ou describe.skipIf().

example.test.ts
ts
const macOS = process.platform === "darwin";

test.skipIf(macOS)("executa em não-macOS", () => {
  // executa se *não* macOS
});

test.todoIf ​

Se você quiser marcar o teste como TODO, use test.todoIf() ou describe.todoIf(). Escolher cuidadosamente skipIf ou todoIf pode mostrar uma diferença entre, por exemplo, intenção de "inválido para este alvo" e "planejado mas não implementado ainda."

example.test.ts
ts
const macOS = process.platform === "darwin";

// TODO: implementamos apenas para Linux até agora.
test.todoIf(macOS)("executa em posix", () => {
  // executa se *não* macOS
});

test.failing ​

Use test.failing() quando souber que um teste está falhando atualmente, mas quiser rastreá-lo e ser notificado quando começar a passar. Isso inverte o resultado do teste:

  • Um teste com falha marcado com .failing() passará
  • Um teste passando marcado com .failing() falhará (com uma mensagem indicando que agora está passando e deve ser corrigido)
ts
// Isso passará porque o teste está falhando como esperado
test.failing("matemática está quebrada", () => {
  expect(0.1 + 0.2).toBe(0.3); // falha devido à precisão de ponto flutuante
});

// Isso falhará com uma mensagem que o teste agora está passando
test.failing("bug corrigido", () => {
  expect(1 + 1).toBe(2); // passa, mas esperávamos que falhasse
});

Isso é útil para rastrear bugs conhecidos que você planeja corrigir mais tarde ou para implementar desenvolvimento orientado a testes.

Testes Condicionais para Blocos Describe ​

Os modificadores condicionais .if(), .skipIf() e .todoIf() também podem ser aplicados a blocos describe, afetando todos os testes dentro da suíte:

example.test.ts
ts
const isMacOS = process.platform === "darwin";

// Executa apenas toda a suíte no macOS
describe.if(isMacOS)("funcionalidades específicas do macOS", () => {
  test("funcionalidade A", () => {
    // executa apenas no macOS
  });

  test("funcionalidade B", () => {
    // executa apenas no macOS
  });
});

// Ignora toda a suíte no Windows
describe.skipIf(process.platform === "win32")("funcionalidades Unix", () => {
  test("funcionalidade C", () => {
    // ignorado no Windows
  });
});

// Marca toda a suíte como TODO no Linux
describe.todoIf(process.platform === "linux")("Suporte Linux próximo", () => {
  test("funcionalidade D", () => {
    // marcado como TODO no Linux
  });
});

Testes Parametrizados ​

test.each e describe.each ​

Para executar o mesmo teste com múltiplos conjuntos de dados, use test.each. Isso cria um teste parametrizado que é executado uma vez para cada caso de teste fornecido.

math.test.ts
ts
const cases = [
  [1, 2, 3],
  [3, 4, 7],
];

test.each(cases)("%p + %p deve ser %p", (a, b, expected) => {
  expect(a + b).toBe(expected);
});

Você também pode usar describe.each para criar uma suíte parametrizada que é executada uma vez para cada caso de teste:

sum.test.ts
ts
describe.each([
  [1, 2, 3],
  [3, 4, 7],
])("add(%i, %i)", (a, b, expected) => {
  test(`retorna ${expected}`, () => {
    expect(a + b).toBe(expected);
  });

  test(`soma é maior que cada valor`, () => {
    expect(a + b).toBeGreaterThan(a);
    expect(a + b).toBeGreaterThan(b);
  });
});

Passagem de Argumentos ​

Como argumentos são passados para sua função de teste depende da estrutura dos seus casos de teste:

  • Se uma linha da tabela é um array (como [1, 2, 3]), cada elemento é passado como um argumento individual
  • Se uma linha não é um array (como um objeto), é passada como um único argumento
example.test.ts
ts
// Itens de array passados como argumentos individuais
test.each([
  [1, 2, 3],
  [4, 5, 9],
])("add(%i, %i) = %i", (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// Itens de objeto passados como um único argumento
test.each([
  { a: 1, b: 2, expected: 3 },
  { a: 4, b: 5, expected: 9 },
])("add($a, $b) = $expected", data => {
  expect(data.a + data.b).toBe(data.expected);
});

Especificadores de Formato ​

Há várias opções disponíveis para formatar o título do teste:

EspecificadorDescrição
%ppretty-format
%sString
%dNúmero
%iInteiro
%fPonto flutuante
%jJSON
%oObjeto
%#Índice do caso de teste
%%Sinal de porcentagem único (%)

Exemplos ​

example.test.ts
ts
// Especificadores básicos
test.each([
  ["hello", 123],
  ["world", 456],
])("string: %s, número: %i", (str, num) => {
  // "string: hello, número: 123"
  // "string: world, número: 456"
});

// %p para saída pretty-format
test.each([
  [{ name: "Alice" }, { a: 1, b: 2 }],
  [{ name: "Bob" }, { x: 5, y: 10 }],
])("usuário %p com dados %p", (user, data) => {
  // "usuário { name: 'Alice' } com dados { a: 1, b: 2 }"
  // "usuário { name: 'Bob' } com dados { x: 5, y: 10 }"
});

// %# para índice
test.each(["apple", "banana"])("fruta #%# é %s", fruit => {
  // "fruta #0 é apple"
  // "fruta #1 é banana"
});

Contagem de Asserções ​

O Bun oferece suporte à verificação de que um número específico de asserções foi chamado durante um teste:

expect.hasAssertions() ​

Use expect.hasAssertions() para verificar que pelo menos uma asserção é chamada durante um teste:

example.test.ts
ts
test("trabalho assíncrono chama asserções", async () => {
  expect.hasAssertions(); // Falhará se nenhuma asserção for chamada

  const data = await fetchData();
  expect(data).toBeDefined();
});

Isso é especialmente útil para testes assíncronos para garantir que suas asserções realmente executem.

expect.assertions(count) ​

Use expect.assertions(count) para verificar que um número específico de asserções é chamado durante um teste:

example.test.ts
ts
test("exatamente duas asserções", () => {
  expect.assertions(2); // Falhará se não exatamente 2 asserções forem chamadas

  expect(1 + 1).toBe(2);
  expect("hello").toContain("ell");
});

Isso ajuda a garantir que todas suas asserções executem, especialmente em código assíncrono complexo com múltiplos caminhos de código.

Teste de Tipo ​

O Bun inclui expectTypeOf para testar tipos TypeScript, compatível com Vitest.

expectTypeOf ​

A função expectTypeOf fornece asserções em nível de tipo que são verificadas pelo verificador de tipo do TypeScript. Para testar seus tipos:

  1. Escreva suas asserções de tipo usando expectTypeOf
  2. Execute bunx tsc --noEmit para verificar se seus tipos estão corretos
example.test.ts
ts
import { expectTypeOf } from "bun:test";

// Asserções de tipo básicas
expectTypeOf<string>().toEqualTypeOf<string>();
expectTypeOf(123).toBeNumber();
expectTypeOf("hello").toBeString();

// Correspondência de tipo de objeto
expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>();

// Tipos de função
function greet(name: string): string {
  return `Hello ${name}`;
}

expectTypeOf(greet).toBeFunction();
expectTypeOf(greet).parameters.toEqualTypeOf<[string]>();
expectTypeOf(greet).returns.toEqualTypeOf<string>();

// Tipos de array
expectTypeOf([1, 2, 3]).items.toBeNumber();

// Tipos de Promise
expectTypeOf(Promise.resolve(42)).resolves.toBeNumber();

Para documentação completa sobre matchers expectTypeOf, veja a Referência da API.

Matchers ​

O Bun implementa os seguintes matchers. Compatibilidade completa com Jest está no roadmap; acompanhe o progresso aqui.

Matchers Básicos ​

StatusMatcher
✅.not
✅.toBe()
✅.toEqual()
✅.toBeNull()
✅.toBeUndefined()
✅.toBeNaN()
✅.toBeDefined()
✅.toBeFalsy()
✅.toBeTruthy()
✅.toStrictEqual()

Matchers de String e Array ​

StatusMatcher
✅.toContain()
✅.toHaveLength()
✅.toMatch()
✅.toContainEqual()
✅.stringContaining()
✅.stringMatching()
✅.arrayContaining()

Matchers de Objeto ​

StatusMatcher
✅.toHaveProperty()
✅.toMatchObject()
✅.toContainAllKeys()
✅.toContainValue()
✅.toContainValues()
✅.toContainAllValues()
✅.toContainAnyValues()
✅.objectContaining()

Matchers de Número ​

StatusMatcher
✅.toBeCloseTo()
✅.closeTo()
✅.toBeGreaterThan()
✅.toBeGreaterThanOrEqual()
✅.toBeLessThan()
✅.toBeLessThanOrEqual()

Matchers de Função e Classe ​

StatusMatcher
✅.toThrow()
✅.toBeInstanceOf()

Matchers de Promise ​

StatusMatcher
✅.resolves()
✅.rejects()

Matchers de Função Mock ​

StatusMatcher
✅.toHaveBeenCalled()
✅.toHaveBeenCalledTimes()
✅.toHaveBeenCalledWith()
✅.toHaveBeenLastCalledWith()
✅.toHaveBeenNthCalledWith()
✅.toHaveReturned()
✅.toHaveReturnedTimes()
✅.toHaveReturnedWith()
✅.toHaveLastReturnedWith()
✅.toHaveNthReturnedWith()

Matchers de Snapshot ​

StatusMatcher
✅.toMatchSnapshot()
✅.toMatchInlineSnapshot()
✅.toThrowErrorMatchingSnapshot()
✅.toThrowErrorMatchingInlineSnapshot()

Matchers de Utilidade ​

StatusMatcher
✅.extend
✅.anything()
✅.any()
✅.assertions()
✅.hasAssertions()

Ainda Não Implementado ​

StatusMatcher
❌.addSnapshotSerializer()

Melhores Práticas ​

Use Nomes de Teste Descritivos ​

example.test.ts
ts
// Bom
test("deve calcular preço total incluindo imposto para múltiplos itens", () => {
  // implementação do teste
});

// Evite
test("cálculo de preço", () => {
  // implementação do teste
});

Agrupe Testes Relacionados ​

auth.test.ts
ts
describe("Autenticação de usuário", () => {
  describe("com credenciais válidas", () => {
    test("deve retornar dados do usuário", () => {
      // implementação do teste
    });

    test("deve definir token de autenticação", () => {
      // implementação do teste
    });
  });

  describe("com credenciais inválidas", () => {
    test("deve lançar erro de autenticação", () => {
      // implementação do teste
    });
  });
});

Use Matchers Apropriados ​

auth.test.ts
ts
// Bom: Use matchers específicos
expect(users).toHaveLength(3);
expect(user.email).toContain("@");
expect(response.status).toBeGreaterThanOrEqual(200);

// Evite: Usar toBe para tudo
expect(users.length === 3).toBe(true);
expect(user.email.includes("@")).toBe(true);
expect(response.status >= 200).toBe(true);

Teste Condições de Erro ​

example.test.ts
ts
test("deve lançar erro para entrada inválida", () => {
  expect(() => {
    validateEmail("not-an-email");
  }).toThrow("Formato de email inválido");
});

test("deve lidar com erros assíncronos", async () => {
  await expect(async () => {
    await fetchUser("invalid-id");
  }).rejects.toThrow("Usuário não encontrado");
});

Use Setup e Teardown ​

example.test.ts
ts
import { beforeEach, afterEach, test } from "bun:test";

let testUser;

beforeEach(() => {
  testUser = createTestUser();
});

afterEach(() => {
  cleanupTestUser(testUser);
});

test("deve atualizar perfil do usuário", () => {
  // Usar testUser no teste
});

Bun by www.bunjs.com.cn edit