Skip to content

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

기본 사용

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

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

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

테스트 그룹화

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

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

비동기 테스트

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

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

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

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

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

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

타임아웃

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

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 테스트에 유용합니다.

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 회 반복).

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 으로 개별 테스트를 건너뛰세요. 이러한 테스트는 실행되지 않습니다.

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

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

test.todo

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

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() 를 사용하세요.

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() 를 사용하세요. 조건이 참이면 테스트가 실행됩니다. 이는 특정 아키텍처나 운영 체제에서만 실행되어야 하는 테스트에 특히 유용합니다.

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

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

test.skipIf

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

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

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

test.todoIf

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

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 블록에도 적용할 수 있습니다.

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.eachdescribe.each

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

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 를 사용하여 각 테스트 케이스마다 한 번씩 실행되는 매개변수화된 스위트를 생성할 수도 있습니다.

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]) 각 요소는 개별 인수로 전달됩니다
  • 행이 배열이 아닌 경우 (예: 객체) 단일 인수로 전달됩니다
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객체
%#테스트 케이스 인덱스
%%단일 퍼센트 기호 (%)

예시

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() 을 사용하여 테스트 중에 최소한 하나의 단언이 호출되었는지 확인하세요.

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

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

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

expect.assertions(count)

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

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 을 실행하여 타입이 올바른지 확인
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()

모범 사례

설명적인 테스트 이름 사용

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

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

관련 테스트 그룹화

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

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

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

적절한 매처 사용

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

오류 조건 테스트

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

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

설정 및 정리 사용

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

let testUser;

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

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

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

Bun by www.bunjs.com.cn 편집