Jest ( + TypeScript )
포스트
취소

Jest ( + TypeScript )

해당 포스트는 Jest + TypeScript에 대해 정리한 포스트입니다.

🫠 설치

테스트 파일이란 __tests__ 폴더 내부의 파일이나 /.test.(js|ts)/인 파일을 의미합니다.

@types/jestTypeScript를 사용하지 않더라도 설치만하면 테스트 파일의 자동완성을 지원해줍니다.

1
2
3
4
npm i -D jest @types/jest

# TS 사용할 거면 설치
npm i -D ts-jest

VSCode를 사용한다면 Jest 익스텐션을 설치하면 테스트 파일을 실행하는데 더 도움이 됩니다.

🦴 세팅

TypeScript를 사용할 것이라고 jest에게 알려주기 위해서 .jest.config.js를 생성해서 아래 코드를 작성해줘야 합니다.

1
2
3
4
5
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
};

🧭 Globals

Globals들이 궁금하다면 jest 공식문서 - Globals를 참고해주세요.

테스트 파일에서 전역적으로 들어가는 메서드와 객체들입니다.
즉, 명시적으로 import 해주지 않아도 자동으로 가져와져서 사용할 수 있습니다.

혹시 명시적으로 가져오는 것을 선호한다면 테스트 파일에 아래와 같이 작성하면 됩니다.

1
import {expect, jest, test} from '@jest/globals';

0️⃣ test(), expect()

  • test(): 테스트를 실행하는 함수
  • expect(): 테스트 대상을 입력하는 함수
1
2
3
4
5
6
7
test("2 + 2 = 4", () => {
  expect(2 + 2).toBe(4);
});

/**
 * √ 2 + 2 = 4 (3 ms)
 */

1️⃣ description()

여러 테스트들을 그룹화하는 목적으로 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
describe("사칙연산 테스트", () => {
  test("2 + 2", () => {
    expect(2 + 2).toBe(4);
  });
  test("2 - 2", () => {
    expect(2 - 2).toBe(0);
  });
  test("2 * 2", () => {
    expect(2 * 2).toBe(4);
  });
  test("2 / 2", () => {
    expect(2 / 2).toBe(1);
  });
});

/**
 * 사칙연산 테스트
 *   √ 2 + 2 (3 ms)
 *   √ 2 - 2 (4 ms)
 *   √ 2 * 2 (3 ms)
 *   √ 2 / 2 (3 ms)
 */

2️⃣ skip

특정 테스트를 제외시키는데 사용합니다.

describe.skip(), test.skip()과 같은 형태로 사용할 수 있습니다.
skip()이 적용된 것은 테스트에서 제외됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
describe("사칙연산 테스트", () => {
  test("2 + 2", () => {
    expect(2 + 2).toBe(4);
  });
  test.skip("2 - 2", () => {
    expect(2 - 2).toBe(0);
  });
  test("2 * 2", () => {
    expect(2 * 2).toBe(4);
  });
  test.skip("2 / 2", () => {
    expect(2 / 2).toBe(1);
  });
});

/**
 * 사칙연산 테스트
 *   √ 2 + 2 (4 ms)
 *   √ 2 * 2 (4 ms)
 *   ○ skipped 2 - 2
 *   ○ skipped 2 / 2
 */

3️⃣ only

특정 테스트만 실행하는데 사용합니다.

describe.only(), test.only()과 같은 형태로 사용할 수 있습니다.
only()를 적용한 것을 제외한 모든 것은 테스트에서 제외됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
describe("사칙연산 테스트", () => {
  test("2 + 2", () => {
    expect(2 + 2).toBe(4);
  });
  test("2 - 2", () => {
    expect(2 - 2).toBe(0);
  });
  test("2 * 2", () => {
    expect(2 * 2).toBe(4);
  });
  test.only("2 / 2", () => {
    expect(2 / 2).toBe(1);
  });
});

/**
 * 사칙연산 테스트
 *   √ 2 / 2 (5 ms)
 *   ○ skipped 2 + 2
 *   ○ skipped 2 - 2
 *   ○ skipped 2 * 2
 */

4️⃣ beforeAll()

모든 테스트 직전에 단 한 번만 실행됩니다.

1
2
3
beforeAll(() => {
  console.log("##### 테스트 직전 단 한 번 실행 #####");
});

5️⃣ afterAll()

모든 테스트 직후에 단 한 번만 실행됩니다.

1
2
3
afterAll(() => {
  console.log("##### 테스트 직후 단 한 번 실행 #####");
});

6️⃣ beforeEach()

각 테스트 전에 실행됩니다.
만약 description()의 내부와 외부에 있다면 외부 먼저 실행됩니다.

1
2
3
beforeEach(() => {
  console.log("##### 각 테스트 전에 실행 #####");
});

7️⃣ afterEach()

각 테스트 전에 실행됩니다.
만약 description()의 내부와 외부에 있다면 내부 먼저 실행됩니다.

1
2
3
afterEach(() => {
  console.log("##### 각 테스트 후에 실행 #####");
});

🗺️ Matchers

Matchers들이 궁금하다면 jest 공식문서 - Matchers를 참고해주세요.

테스트 대상과 일치하는지 여부를 판단할 때 사용하는 메서드입니다.

정확히 일치, 부분 일치, 포함, 예외 등의 많은 Matcher가 있기 때문에 테스트의 목적에 따라 찾아서 선택하면 됩니다.
여기서는 대표적인 몇 가지 Matcher에 대해 작성하겠습니다.

아래 함수들을 사용해서 테스트한다고 가정하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
type SumHandler = (...args: number[]) => number;
/** 2023/03/12 - 더하기 함수 - by 1-blue */
export const sum: SumHandler = (...args) =>
  args.reduce((acc, curr) => acc + curr);

type MakeObjectHandler = <T1, T2>(name: T1, age: T2) => { name: T1; age: T2 };
/** 2023/03/12 - 객체 생성 함수 - by 1-blue */
export const makeObject: MakeObjectHandler = (name, age) => ({ name, age });

type MakeArrayHandler = <T>(...args: T[]) => T[];
/** 2023/03/12 - 배열 생성 함수 - by 1-blue */
export const makeArray: MakeArrayHandler = (...args) => args;

type MakeThrowHandler = () => never;
/** 2023/03/12 - 예외 던지기 함수 - by 1-blue */
export const makeThrow: MakeThrowHandler = () => {
  throw new Error("대충 예외");
};

type FetchCallbackHandler = (
  callback: (name: string) => void,
  time?: number
) => void;
/** 2023/03/12 - "callback"을 이용한 비동기 - by 1-blue */
export const fetchNameByCallback: FetchCallbackHandler = (
  callback,
  time = 1000
) => setTimeout(() => callback("Aatrox"), time);

type FetchPromiseHandler = (time?: number) => Promise<string>;
/** 2023/03/12 - "Promise"를 이용한 비동기 - by 1-blue */
export const fetchNameByPromise: FetchPromiseHandler = (time = 1000) =>
  new Promise((resolve, reject) => setTimeout(() => resolve("Aatrox"), time));

0️⃣ 일치 테스트

toBe()를 이용해서 단순 일치 여부를 테스트합니다.
만약 객체나 배열같은 참조형의 내부 모든 값을 비교하기 위해서는 toEqual(), toStrictEqual()을 사용해야 합니다.

1
2
3
4
5
6
7
8
9
10
describe("더하기 테스트", () => {
  test("1 ~ 5의 합은 15", () => {
    expect(sum(1, 2, 3, 4, 5)).toBe(15);
  });
});

/**
 * 더하기 테스트
 *   √ 1 ~ 5의 합은 15 (2 ms)
 */

1️⃣ 프로퍼티 일치 테스트

toEqual(), toStrictEqual()를 이용해서 객체나 배열의 프로퍼티의 일치에 대해 확인합니다.

  • toEqual(): 프로퍼티가 일치하는지 여부만 확인 ( 추가적으로 더 있어도 성공 )
  • toStrictEqual(): 프로퍼티가 정확하게 일치하는지 확인 ( 추가적으로 더 있으면 실패 )
1
2
3
4
5
6
7
8
9
describe("객체 테스트", () => {
  test("toEqual으로 객체 비교", () => {
    expect(makeObject("Aatrox", 26)).toStrictEqual({ name: "Aatrox", age: 26 });
  });
});
/**
 * 객체 테스트
 *   √ toEqual으로 객체 비교 (1 ms)
 */

2️⃣ 참/거짓 테스트

toBeFalsy()toBeTruthy()를 이용해서 참과 거짓을 테스트합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe("불리언 테스트", () => {
  test("falsy 테스트", () => {
    expect(sum(1, -1)).toBeFalsy();
  });
  test("truthy 테스트", () => {
    expect(sum(1, 0)).toBeTruthy();
  });
});

/**
 * 불리언 테스트
 *   √ falsy 테스트 (1 ms)
 *   √ truthy 테스트
 */

3️⃣ 대소/근사치 테스트

toBeLessThan(), toBeLessThanOrEqual(), toBeGreaterThan(), toBeGreaterThanOrEqual()를 이용해서 대소 비교 테스트를 합니다.
또한 소수점을 정확하게 연산하지 못하는 경우가 있기 때문에 toBeCloseTo()를 제공해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
describe("대소비교", () => {
  const ID = "123";

  test("3보다 작다 ( X )", () => {
    expect(ID.length).toBeLessThan(3);
  });
  test("3보다 작거나 같다 ( O )", () => {
    expect(ID.length).toBeLessThanOrEqual(3);
  });
  test("3보다 크다 ( X )", () => {
    expect(ID.length).toBeGreaterThan(3);
  });
  test("3보다 크거나 같다 ( O )", () => {
    expect(ID.length).toBeGreaterThanOrEqual(3);
  });
  test("근사치 검사", () => {
    expect(0.1 + 0.2).toBeCloseTo(0.3);
  });
});

/**
 * 대소비교
 *   × 3보다 작다 ( X ) (3 ms)
 *   √ 3보다 작거나 같다 ( O )
 *   × 3보다 크다 ( X ) (1 ms)
 *   √ 3보다 크거나 같다 ( O )
 *   √ 근사치 검사 (1 ms)
 */

4️⃣ 문자열 테스트

toMatch()를 이용해서 일단 문자열 혹은 정규 표현식을 이용해서 문자열을 테스트할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe("문자열 테스트", () => {
  test("정규 표현식 ( O )", () => {
    expect("Hello, Jest").toMatch(/h/i);
  });
  test("정규 표현식 ( X )", () => {
    expect("Hello, Jest").toMatch(/h/);
  });
});

/**
 * 문자열 테스트
 *   √ 정규 표현식 ( O ) (1 ms)
 *   × 정규 표현식 ( X ) (1 ms)
 */

5️⃣ 배열 테스트

toContain()를 이용해서 배열의 포함 여부를 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
describe("배열 테스트", () => {
  test("포함 여부", () => {
    expect(makeArray(1, 2, 3, 4, 5)).toContain(5);
  });
});

/**
 * 배열 테스트
 *   √ 포함 여부
 */

6️⃣ 예외 테스트

toThrow()를 이용해서 예외가 발생했는지 그리고 예외 메세지가 일치하는지 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe("예외 테스트", () => {
  test("그냥 예외 확인", () => {
    expect(() => makeThrow()).toThrow();
  });
  test("예외 메세지 확인", () => {
    expect(() => makeThrow()).toThrow("대충 예외");
  });
});

/**
 * 예외 테스트
 *   √ 그냥 예외 확인 (2 ms)
 *   √ 예외 메세지 확인
 */

7️⃣ 비동기 테스트

(1)처럼 콜백을 이용한 비동기를 테스트하는 경우 test()의 두 번째 인자인 함수의 첫 번째 인자로 받은 함수(done)을 이용하면 done()을 실행할 때까지 기다립니다.

(2)처럼 Promise를 테스트하는 경우 return을 사용해야 합니다.
만약 then()을 이용하기 싫다면 resolves를 이용해서 비교할 수 있습니다.

(3)처럼 async/await로 테스트하는 경우 일반적으로 사용하던 형식에 맞게 사용하면 됩니다.
혹은 resolves를 이용해서 동일하게 처리할 수 있습니다.

resolves처럼 rejects도 존재합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
describe("비동기 테스트", () => {
  // (1)
  test("callback 테스트", (done) => {
    fetchNameByCallback((name) => {
      expect(name).toBe("Aatrox");
      done();
    });
  });

  // "promise"를 사용하면 "return" | "resolves" & "rejects" 사용
  // (2)
  test("return를 사용한 promise 테스트", () => {
    return fetchNameByPromise().then((name) => expect(name).toBe("Aatrox"));
  });
  // (2)
  test("resloves를 사용한 promise 테스트", () => {
    return expect(fetchNameByPromise()).resolves.toBe("Aatrox");
  });

  // (3)
  test("async/await을 사용한 promise 테스트 - 1", async () => {
    const name = await fetchNameByPromise();
    expect(name).toBe("Aatrox");
  });
  // (3)
  test("async/await을 사용한 promise 테스트 - 2", async () => {
    await expect(fetchNameByPromise()).resolves.toBe("Aatrox");
  });
});

/**
 * 비동기 테스트
 *   √ callback 테스트 (1002 ms)
 *   √ return를 사용한 promise 테스트 (1014 ms)
 *   √ resloves를 사용한 promise 테스트 (1015 ms)
 *   √ async/await을 사용한 promise 테스트 - 1 (1015 ms)
 *   √ async/await을 사용한 promise 테스트 - 2 (1037 ms)
 */

8️⃣ 반대로 처리

not을 사용하면 테스트 결과를 반대로 처리합니다.

1
2
3
4
5
6
7
test("10은 10이 아닌지...? ( X )", () => {
  expect(10).not.toBe(10);
});

/**
 * × 10은 10이 아닌지...? ( X )
 */

📮 레퍼런스

  1. jest 공식문서 - Globals
  2. jest 공식문서 - Matchers
  3. Youtube - 코딩앙마 - Jest 강좌

  4. 1-blue - Promise
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.