-
[Jest] 기본 활용법React/jest 2023. 8. 3. 14:38
설치 및 세팅
설치
npm install jest --save-dev
- 개발환경에서만 테스트를 사용을 하니 개발환경에 설치
세팅
- npm test를 치면 테스트가 가능
- ~.test.js 에 해당하는 파일을 전부 검사
참고 : https://www.youtube.com/watch?v=g4MdUjxA-S4
Matcher
toBe
test("1은 1이야", () => { expect(1).toBe(1); });
- expect : test할 값
- toBe : 예상하는 값
test("1은 2가 아니야", () => { expect(1).not.toBe(2); });
- 부정값을 예상하는 경우
test("0.1+0.2 = 0.3??", () => { expect(fn.add(0.1, 0.2)).toBeCloseTo(0.3); });
- javascript 소수 계산시 무한소수가 되기 때문에 toBeCloseTo사용
toBeCloseTo를 사용해야 하는 이유 -> 부동소수점
부동소수점?
부동소수점이란 실수를 표현할 때 소수점의 위치를 고정하지 않는 것을 말한다.
부동 소수점(floating point) 방식은 소수점 (point) 이 둥둥 떠다닌다 (floating) 라는 의미로, 표현할 수 있는 값을 범위를 최대한 넓혀 오차를 줄이자는 시도에서 탄생한 녀석이다.
부동 소수점은 고정 소수점 방식과는 달리 메모리를 가수부(23bit)와 지수부(8bit)로 나눈다.
기수부 에는 실제 실수 데이터 비트들이 들어가고, 지수부에는 소수점의 위치를 가리키는 제곱승 이 들어간다고 보면 된다.
뭔가 직관적이지 않아 오히려 번거롭게 보일수도 있곘지만, 이런식으로 실수를 표현하는 이유는 큰 범위의 값을 표현하기 위해 서이다.
자세히 알아보기그외
- toBeNull
- toBeDefined
- toBeTruty
- toBeFalsy
- toBeGreaterThan
- toBeGeaterThanOrEqual
- toBeLessThan
- toBeLessThanOrEqual
toEqual
//fn.js const fn ={ makeUser: (name, age, gender) => ({ name, age, gender: undefined }), }
- user를 만드는 함수를 정의
//fn.test.js test("toBe : name and age to object", () => { expect(fn.makeUser("Mike", 30)).toBe({ name: "Mike", age: 30, }); });
- 에러가 나오는 이유는 toBe는 같은 객체인지 확인
- 위에는 내용이 같을 뿐 다른 객체
test("ToEqual : name and age to object", () => { expect(fn.makeUser("Mike", 30)).toEqual({ name: "Mike", age: 30, }); });
- 내용이 같은지 확인을 할 때는 toEqual사용
//fn.test.js test("toStrictEqual : name and age to object", () => { expect(fn.makeUser("Mike", 30)).toStrictEqual({ name: "Mike", age: 30, }); });
- strictEqual로 할 시에는 gender가 undefined상태이기 때문에 테스트에 실패
toMatch
test("check the letter with RegEx ", () => { expect("hello world").toMatch(/h/); });
- 정규표현식을 사용해서 문자열 체크가 가능
toContain
test("check the list ", () => { const user = "Mike"; const userList = ["Tom", "Jane", "Kai"]; expect(userList).toContain("Kai"); });
- 베열안에 요소 확인 가능
toThrow
//fn.js const fn = { throwErr: () => { throw new Error("xx"); } }; //fn.test.js test("check the err ", () => { expect(() => fn.throwErr()).toThrow("xx"); });
- 설정한 에러메시지대로 출력이 됐는지 확인 가능
비동기 함수 결과
callback
//fn.js const fn = { getName: (callback) => { const name = "Mike"; setTimeout(() => { callback(name); }, 3000); } } //fn.test.js test("async func ", (done) => { function callback(name) { expect(name).toBe("Mike"); done(); } fn.getName(callback); });
- callback을 사용하는 경우
- 이제는 거의 사용 x
promise
//fn.js const fn = { getAge: () => { const age = 30; return new Promise((res, rej) => { setTimeout(() => { res(age); }, 3000); }); } } //fn.test.js //방법1 test("async func ", () => { return fn.getAge().then((age) => { expect(age).toBe(30); }); }); //방법2 test("async func ", () => { return expect(fn.getAge()).resolves.toBe(30); });
- 테스트에서 return을 안걸면 비동기작업이 이루어지지 않아서 테스트를 통과해버리는 상황이 벌어짐 - 소요시간이 1ms
- return을 걸면 비동기처리가 잘 작동(settimeout 3 sec)하며 테스트
async await
test("check the promise ", async () => { const age = await fn.getAge(); expect(age).toBe(30); }); test("check the promise ", async () => { await expect(fn.getAge()).resolves.toBe(30); });
- async await를 테스트 하는 방법
테스트 전후 처리
각 테스트 전후
beforeEach, afterEach
//각 테스트 전후 변수 초기화 let num = 0; beforeEach(() => { num = 0; }); test(" 0 + 1 = 1? ", () => { num = fn.add(num, 1); expect(num).toBe(1); }); test(" 0 + 2 = 2? ", () => { num = fn.add(num, 2); expect(num).toBe(2); }); test(" 0 + 3 = 3? ", () => { num = fn.add(num, 3); expect(num).toBe(3); });
전체 테스트 전후
beforeAll, afterAll
// 테스트 전 db connect , 테스트 후 db disconnect beforeAll(async ()=>{ user = await fn.connectUserDb() }) afterAll(async ()=>{ user = await fn.disConnectUserDb() })
describe 관련 테스트 하나로 묶기
describe(" 숫자 체크 ", () => { test(" 1? ", () => { expect(1).toBe(1); }); test(" 0? ", () => { expect(0).toBe(0); }); });
여러 테스트 중 하나만 테스트
describe(" 숫자 체크 ", () => { test.only(" 1? ", () => { expect(1).toBe(1); }); test(" 0? ", () => { expect(0).toBe(0); }); });
- test.only사용
여러 테스트 중 특정 테스트 제외
describe(" 숫자 체크 ", () => { test(" 1? ", () => { expect(1).toBe(1); }); test.skip(" 0? ", () => { expect(0).toBe(0); }); });
- test.skip사용
Mocking
- 외부 함수에 의존하지 않고 테스트 하고자할 때
- user db 에서 가져와야 하는 경우 등 - 외부 요인(네트워크)에 의해 영향 많이 받음
const mockFn = jest.fn(); mockFn(); mockFn(1); test(" 0 + 3 = 3? ", () => { console.log(mockFn.mock.calls); let num = 0; num = fn.add(num, 3); expect(num).toBe(3); }); test("함수는 2번 호출됩니다. ", () => { expect(mockFn.mock.calls.length).toBe(2); }); test("2번째로 호출된 함수에 전달된 첫번째 인수슨 1입니다. ", () => { expect(mockFn.mock.calls[1][0]).toBe(1); });
- 함수에서 mockFn.mock.calls을 찍어보면 위처림 [ [ ] , [1]]이 나온다.
- 첫번째 mockFn()에서 받은 인자가 없어서 빈리스트
- 첫번째 mockFn()에서 받은 인자가 1이라 리스트 안에 1
- mockFn.mock.calls.length : 2번 호출 해서 2
- mockFn.mock.calls[1][0] : 호출한 mock 중에 2번째 함수의 인자는 1
// 별도의 함수 만들지 않아도 테스트 가능 - 1을 더하는 콜백함수 없이 테스트 const mockFn = jest.fn(); function forEachAdd1(arr) { arr.forEach((num) => { mockFn(num + 1); }); } forEachAdd1([10, 20, 30]); test("함수는 3번 호출됩니다.", () => { expect(mockFn.mock.calls.length).toBe(3); }); test("전달된 값은 11, 21, 31입니다. ", () => { expect(mockFn.mock.calls[0][0]).toBe(11); expect(mockFn.mock.calls[1][0]).toBe(21); expect(mockFn.mock.calls[2][0]).toBe(31); });
- 별도의 파일에 함수를 만들지 않고 테스트 내부에서 함수를 정의해도 테스트 가능
- loop를 3번 돌때마다 mock이 일어나서 length = 3
- 각각에 인자는 배열의 인자 +1
const mockFn = jest.fn((num) => num + 1); mockFn(10); mockFn(20); mockFn(30); test("10에서 1증가한 값이 반환 ", () => { expect(mockFn.mock.results[0].value).toBe(11); }); test("20에서 1증가한 값이 반환 ", () => { expect(mockFn.mock.results[1].value).toBe(21); }); test("20에서 1증가한 값이 반환 ", () => { expect(mockFn.mock.results[2].value).toBe(31); });
- jest.fn()에 익명함수를 정의하는 방법도 있음
비동기 함수 mock
const mockFn = jest.fn(); mockFn.mockResolvedValue({ name: "mike" }); test("비동기 콜백함수로 받아온 결과는 mike ", () => { mockFn().then((res) => { expect(res.name).toBe("mike"); }); });
- 콜백함수의 결과를 임의로 지정
- 지정한 값에 대해서 resolve된 결과를 미리 지정
- 그 값을 예측값과 비교
DB 값 삽입 없이 createUser테스트
jest.mock("./fn"); fn.createUser.mockReturnValue({ name: "mike" }); test("유저를 만든다", () => { const user = fn.createUser("mike"); expect(user.name).toBe("mike"); });
- 해당 함수에서 resolve가 잘되었다는 것을 전제로 결과 값을 지정
- 해당 함수가 실행이 되는 것이 아니기 때문에 db에 실제로 정보가 삽입되지 않음
기타 mocking method
const mockFn = jest.fn(); mockFn(10, 20); mockFn(); mockFn(30, 40); test("한번 이상 호출?", () => { expect(mockFn).toBeCalled(); }); test("정확히 3번 호출?", () => { expect(mockFn).toBeCalledTimes(3); }); test("10이랑 20을 인수로 전달받은 함수가 있는가", () => { expect(mockFn).toBeCalledWith(10, 20); }); test("마지막 함수는 30이랑 40 받았음?", () => { expect(mockFn).lastCalledWith(30, 40); });
- toBeCalled : 한 번이라도 호출
- toBeCalledTimes : 몇 번 mock이 호출되었나
- toBeCalledWith : 특정 인수를 전달받은 함수가 있는지
- lastCalledWith : 마지막 호출받을 때 특정 인수 받았는지
'React > jest' 카테고리의 다른 글
[React Testing Library] msw를 활용한 mock API 테스트 (0) 2023.08.04 [React Testing Library] 기본 활용법, 유저 이벤트 테스트 (0) 2023.08.04