Skip to content

내장 bun:test 모듈에서 가져온 Jest 유사 API 로 테스트를 정의합니다. 장기적으로 Bun 은 완전한 Jest 호환성을 목표로 합니다. 현재는 제한된 세트의 expect 매처만 지원됩니다.

기본 사용 ​

간단한 테스트를 정의하려면.

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

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

테스트 그룹화 ​

테스트는 describe 로 스위트에 그룹화할 수 있습니다.

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

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

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

비동기 테스트 ​

테스트는 비동기일 수 있습니다.

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);
});

또는 완료 신호를 보내기 위해 done 콜백을 사용할 수 있습니다. 테스트 정의에서 매개변수로 done 콜백을 포함하면 호출해야 하며 그렇지 않으면 테스트가 중단됩니다.

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();
  });
});

타임아웃 ​

test 에 세 번째 인수로 숫자를 전달하여 테스트당 타임아웃을 밀리초 단위로 선택적으로 지정할 수 있습니다.

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

test("wat", async () => {
  const data = await slowOperation();
  expect(data).toBe(42);
}, 500); // 테스트는 500ms 미만으로 실행되어야 함

bun:test 에서 테스트 타임아웃은 잡을 수 없는 예외를 던져 테스트가 중지되고 실패하도록 강제합니다. 또한 좀비 프로세스가 백그라운드에서 숨어있는 것을 방지하기 위해 테스트에서 생성된 모든 하위 프로세스도 종료합니다.

각 테스트의 기본 타임아웃은 이 타임아웃 옵션이나 jest.setDefaultTimeout() 으로 재정의되지 않는 한 5000ms(5 초) 입니다.

재시도 및 반복 ​

test.retry ​

retry 옵션을 사용하여 테스트가 실패하면 자동으로 재시도하세요. 테스트는 지정된 시도 횟수 내에 성공하면 통과합니다. 이는 간헐적으로 실패할 수 있는 flaky 테스트에 유용합니다.

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

test(
  "flaky 네트워크 요청",
  async () => {
    const response = await fetch("https://example.com/api");
    expect(response.ok).toBe(true);
  },
  { retry: 3 }, // 테스트가 실패하면 최대 3 회 재시도
);

test.repeats ​

repeats 옵션을 사용하여 통과/실패 상태와 관계없이 테스트를 여러 번 실행하세요. 반복 중 어느 하나라도 실패하면 테스트는 실패합니다. 이는 flaky 테스트를 감지하거나 스트레스 테스트에 유용합니다. repeats: N 은 테스트를 총 N+1 회 실행합니다 (초기 1 회 + N 회 반복).

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

test(
  "테스트가 안정적임을 확인",
  () => {
    expect(Math.random()).toBeLessThan(1);
  },
  { repeats: 20 }, // 총 21 회 실행 (초기 1 회 + 20 회 반복)
);

NOTE

동일한 테스트에서 `retry` 와 `repeats` 를 모두 사용할 수 없습니다.

🧟 좀비 프로세스 킬러 ​

테스트가 타임아웃되고 Bun.spawn, Bun.spawnSync 또는 node:child_process 를 통해 테스트에서 생성된 프로세스가 종료되지 않으면 자동으로 종료되고 콘솔에 메시지가 기록됩니다. 이는 타임아웃된 테스트 후 백그라운드에서 좀비 프로세스가 남아있는 것을 방지합니다.

테스트 한정자 ​

test.skip ​

test.skip 으로 개별 테스트를 건너뛰세요. 이러한 테스트는 실행되지 않습니다.

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

test.skip("wat", () => {
  // TODO: 수정 필요
  expect(0.1 + 0.2).toEqual(0.3);
});

test.todo ​

test.todo 로 테스트를 todo 로 표시하세요. 이러한 테스트는 실행되지 않습니다.

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

test.todo("fix this", () => {
  myTestFunction();
});

todo 테스트를 실행하고 통과하는 테스트를 찾으려면 bun test --todo 를 사용하세요.

bash
bun test --todo
my.test.ts:
✗ unimplemented feature
  ^ 이 테스트는 todo 로 표시되었지만 통과합니다. `.todo` 를 제거하거나 테스트가 올바른지 확인하세요.

 0 pass
 1 fail
 1 expect() calls

이 플래그를 사용하면 실패하는 todo 테스트는 오류를 발생시키지 않지만 통과하는 todo 테스트는 실패로 표시되어 todo 표시를 제거하거나 테스트를 수정할 수 있습니다.

test.only ​

특정 테스트나 스위트만 실행하려면 test.only() 또는 describe.only() 를 사용하세요.

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

test("test #1", () => {
  // 실행되지 않음
});

test.only("test #2", () => {
  // 실행됨
});

describe.only("only", () => {
  test("test #3", () => {
    // 실행됨
  });
});

다음 명령은 테스트 #2 와 #3 만 실행합니다.

bash
bun test --only

다음 명령은 테스트 #1, #2, #3 만 실행합니다.

bash
bun test

test.if ​

테스트를 조건부로 실행하려면 test.if() 를 사용하세요. 조건이 참이면 테스트가 실행됩니다. 이는 특정 아키텍처나 운영 체제에서만 실행되어야 하는 테스트에 특히 유용합니다.

example.test.ts
ts
test.if(Math.random() > 0.5)("절반의 시간에 실행", () => {
  // ...
});

const macOS = process.platform === "darwin";
test.if(macOS)("macOS 에서 실행", () => {
  // macOS 이면 실행
});

test.skipIf ​

대신 일부 조건에 따라 테스트를 건너뛰려면 test.skipIf() 또는 describe.skipIf() 를 사용하세요.

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

test.skipIf(macOS)("macOS 가 아닌 곳에서 실행", () => {
  // macOS 가 아니면 실행
});

test.todoIf ​

대신 테스트를 TODO 로 표시하려면 test.todoIf() 또는 describe.todoIf() 를 사용하세요. skipIf 또는 todoIf 를 신중하게 선택하면 예를 들어 "이 대상에 대해 무효"와 "아직 계획되었지만 구현되지 않음"의 차이를 보여줄 수 있습니다.

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

// TODO: 아직 Linux 에 대해서만 구현했습니다.
test.todoIf(macOS)("posix 에서 실행", () => {
  // macOS 가 아니면 실행
});

test.failing ​

테스트가 현재 실패하고 있지만 추적하고 통과하기 시작할 때 알림을 받으려면 test.failing() 을 사용하세요. 이는 테스트 결과를 반전합니다.

  • .failing() 으로 표시된 실패하는 테스트는 통과합니다
  • .failing() 으로 표시된 통과하는 테스트는 실패합니다 (이제 통과하므로 수정해야 한다는 메시지와 함께)
ts
// 테스트가 예상대로 실패하므로 통과합니다
test.failing("math 가 고장남", () => {
  expect(0.1 + 0.2).toBe(0.3); // 부동 소수점 정밀도로 인해 실패
});

// 이제 통과한다는 메시지와 함께 실패합니다
test.failing("수정된 버그", () => {
  expect(1 + 1).toBe(2); // 통과하지만 실패할 것으로 예상됨
});

이는 나중에 수정할 계획인 알려진 버그를 추적하거나 테스트 주도 개발을 구현하는 데 유용합니다.

Describe 블록의 조건부 테스트 ​

조건부 한정자 .if(), .skipIf() 및 .todoIf() 는 스위트 내의 모든 테스트에 영향을 미치는 describe 블록에도 적용할 수 있습니다.

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

// macOS 에서만 전체 스위트 실행
describe.if(isMacOS)("macOS 특정 기능", () => {
  test("기능 A", () => {
    // macOS 에서만 실행
  });

  test("기능 B", () => {
    // macOS 에서만 실행
  });
});

// Windows 에서 전체 스위트 건너뛰기
describe.skipIf(process.platform === "win32")("Unix 기능", () => {
  test("기능 C", () => {
    // Windows 에서 건너뜀
  });
});

// Linux 에서 전체 스위트를 TODO 로 표시
describe.todoIf(process.platform === "linux")("다가오는 Linux 지원", () => {
  test("기능 D", () => {
    // Linux 에서 TODO 로 표시
  });
});

매개변수화된 테스트 ​

test.each 및 describe.each ​

여러 데이터 세트로 동일한 테스트를 실행하려면 test.each 를 사용하세요. 이는 제공된 각 테스트 케이스마다 한 번씩 실행되는 매개변수화된 테스트를 생성합니다.

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

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

describe.each 를 사용하여 각 테스트 케이스마다 한 번씩 실행되는 매개변수화된 스위트를 생성할 수도 있습니다.

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

  test(`합이 각 값보다 큼`, () => {
    expect(a + b).toBeGreaterThan(a);
    expect(a + b).toBeGreaterThan(b);
  });
});

인수 전달 ​

인수가 테스트 함수에 전달되는 방식은 테스트 케이스의 구조에 따라 다릅니다.

  • 테이블 행이 배열인 경우 (예: [1, 2, 3]) 각 요소는 개별 인수로 전달됩니다
  • 행이 배열이 아닌 경우 (예: 객체) 단일 인수로 전달됩니다
example.test.ts
ts
// 배열 항목이 개별 인수로 전달됨
test.each([
  [1, 2, 3],
  [4, 5, 9],
])("add(%i, %i) = %i", (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// 객체 항목이 단일 인수로 전달됨
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);
});

형식 지정자 ​

테스트 제목을 포맷팅하기 위해 사용할 수 있는 여러 옵션이 있습니다.

지정자설명
%ppretty-format
%s문자열
%d숫자
%i정수
%f부동 소수점
%jJSON
%o객체
%#테스트 케이스 인덱스
%%단일 퍼센트 기호 (%)

예시 ​

example.test.ts
ts
// 기본 지정자
test.each([
  ["hello", 123],
  ["world", 456],
])("string: %s, number: %i", (str, num) => {
  // "string: hello, number: 123"
  // "string: world, number: 456"
});

// pretty-format 출력을 위한 %p
test.each([
  [{ name: "Alice" }, { a: 1, b: 2 }],
  [{ name: "Bob" }, { x: 5, y: 10 }],
])("user %p with data %p", (user, data) => {
  // "user { name: 'Alice' } with data { a: 1, b: 2 }"
  // "user { name: 'Bob' } with data { x: 5, y: 10 }"
});

// 인덱스를 위한 %#
test.each(["apple", "banana"])("fruit #%# is %s", fruit => {
  // "fruit #0 is apple"
  // "fruit #1 is banana"
});

단언 카운팅 ​

Bun 은 테스트 중에 특정 수의 단언이 호출되었는지 확인하는 것을 지원합니다.

expect.hasAssertions() ​

expect.hasAssertions() 을 사용하여 테스트 중에 최소한 하나의 단언이 호출되었는지 확인하세요.

example.test.ts
ts
test("비동기 작업이 단언 호출", async () => {
  expect.hasAssertions(); // 단언이 호출되지 않으면 실패

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

이는 단언이 실제로 실행되는지 확인하기 위해 비동기 테스트에 특히 유용합니다.

expect.assertions(count) ​

expect.assertions(count) 를 사용하여 테스트 중에 특정 수의 단언이 호출되었는지 확인하세요.

example.test.ts
ts
test("정확히 2 개의 단언", () => {
  expect.assertions(2); // 정확히 2 개의 단언이 호출되지 않으면 실패

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

이는 여러 코드 경로가 있는 복잡한 비동기 코드에서 모든 단언이 실행되는지 확인하는 데 도움이 됩니다.

타입 테스트 ​

Bun 은 Vitest 와 호환되는 타입 테스트를 위한 expectTypeOf 를 포함합니다.

expectTypeOf ​

expectTypeOf 함수는 TypeScript 타입 체커에 의해 확인되는 타입 수준 단언을 제공합니다. 타입을 테스트하려면.

  1. expectTypeOf 를 사용하여 타입 단언 작성
  2. bunx tsc --noEmit 을 실행하여 타입이 올바른지 확인
example.test.ts
ts
import { expectTypeOf } from "bun:test";

// 기본 타입 단언
expectTypeOf<string>().toEqualTypeOf<string>();
expectTypeOf(123).toBeNumber();
expectTypeOf("hello").toBeString();

// 객체 타입 매칭
expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>();

// 함수 타입
function greet(name: string): string {
  return `Hello ${name}`;
}

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

// 배열 타입
expectTypeOf([1, 2, 3]).items.toBeNumber();

// Promise 타입
expectTypeOf(Promise.resolve(42)).resolves.toBeNumber();

expectTypeOf 매처에 대한 전체 문서는 API 참조 를 참조하세요.

매처 ​

Bun 은 다음 매처를 구현합니다. 완전한 Jest 호환성은 로드맵에 있습니다. 진행 상황 추적.

기본 매처 ​

상태매처
✅.not
✅.toBe()
✅.toEqual()
✅.toBeNull()
✅.toBeUndefined()
✅.toBeNaN()
✅.toBeDefined()
✅.toBeFalsy()
✅.toBeTruthy()
✅.toStrictEqual()

문자열 및 배열 매처 ​

상태매처
✅.toContain()
✅.toHaveLength()
✅.toMatch()
✅.toContainEqual()
✅.stringContaining()
✅.stringMatching()
✅.arrayContaining()

객체 매처 ​

상태매처
✅.toHaveProperty()
✅.toMatchObject()
✅.toContainAllKeys()
✅.toContainValue()
✅.toContainValues()
✅.toContainAllValues()
✅.toContainAnyValues()
✅.objectContaining()

숫자 매처 ​

상태매처
✅.toBeCloseTo()
✅.closeTo()
✅.toBeGreaterThan()
✅.toBeGreaterThanOrEqual()
✅.toBeLessThan()
✅.toBeLessThanOrEqual()

함수 및 클래스 매처 ​

상태매처
✅.toThrow()
✅.toBeInstanceOf()

Promise 매처 ​

상태매처
✅.resolves()
✅.rejects()

목 함수 매처 ​

상태매처
✅.toHaveBeenCalled()
✅.toHaveBeenCalledTimes()
✅.toHaveBeenCalledWith()
✅.toHaveBeenLastCalledWith()
✅.toHaveBeenNthCalledWith()
✅.toHaveReturned()
✅.toHaveReturnedTimes()
✅.toHaveReturnedWith()
✅.toHaveLastReturnedWith()
✅.toHaveNthReturnedWith()

스냅샷 매처 ​

상태매처
✅.toMatchSnapshot()
✅.toMatchInlineSnapshot()
✅.toThrowErrorMatchingSnapshot()
✅.toThrowErrorMatchingInlineSnapshot()

유틸리티 매처 ​

상태매처
✅.extend
✅.anything()
✅.any()
✅.assertions()
✅.hasAssertions()

아직 구현되지 않음 ​

상태매처
❌.addSnapshotSerializer()

모범 사례 ​

설명적인 테스트 이름 사용 ​

example.test.ts
ts
// 좋음
test("세금을 포함한 총 가격 계산을 여러 항목에 대해 수행", () => {
  // 테스트 구현
});

// 피하기
test("가격 계산", () => {
  // 테스트 구현
});

관련 테스트 그룹화 ​

auth.test.ts
ts
describe("사용자 인증", () => {
  describe("유효한 자격 증명", () => {
    test("사용자 데이터 반환", () => {
      // 테스트 구현
    });

    test("인증 토큰 설정", () => {
      // 테스트 구현
    });
  });

  describe("유효하지 않은 자격 증명", () => {
    test("인증 오류 발생", () => {
      // 테스트 구현
    });
  });
});

적절한 매처 사용 ​

auth.test.ts
ts
// 좋음: 특정 매처 사용
expect(users).toHaveLength(3);
expect(user.email).toContain("@");
expect(response.status).toBeGreaterThanOrEqual(200);

// 피하기: 모든 것에 toBe 사용
expect(users.length === 3).toBe(true);
expect(user.email.includes("@")).toBe(true);
expect(response.status >= 200).toBe(true);

오류 조건 테스트 ​

example.test.ts
ts
test("유효하지 않은 입력에 대해 오류 발생", () => {
  expect(() => {
    validateEmail("not-an-email");
  }).toThrow("잘못된 이메일 형식");
});

test("비동기 오류 처리", async () => {
  await expect(async () => {
    await fetchUser("invalid-id");
  }).rejects.toThrow("사용자를 찾을 수 없음");
});

설정 및 정리 사용 ​

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

let testUser;

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

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

test("사용자 프로필 업데이트", () => {
  // 테스트에서 testUser 사용
});

Bun by www.bunjs.com.cn 편집