해당 포스트는
Jest
+TypeScript
에 대해 정리한 포스트입니다.
🫠 설치
테스트 파일이란
__tests__
폴더 내부의 파일이나/.test.(js|ts)/
인 파일을 의미합니다.
@types/jest
는 TypeScript
를 사용하지 않더라도 설치만하면 테스트 파일의 자동완성을 지원해줍니다.
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 )
*/