테스트하기 위해 흉내만 내는 함수이다. 외부요인(네트워크 환경, DB 상태 등)의 영향을 받은 코드를 테스트할 때 주로 사용한다.
jest.fn()
: mock functiton(가짜 함수) 생성.mock
: 호출되었던 값들이 저장됨.mock
property는 각 호출에 대한 this
값도 추적하므로 이를 검사할 수 있음 .calls
: 함수가 총 몇 번 호출되었는가, 호출될 때 전달된 인수가 무엇인가 기억함const mockFn = jest.fn(); // mock 함수 생성
mockFn(); // 호출
mockFn(1); // 인자를 넘겨 호출
mockFn("a");
mockFn([1, 2], { a: "b" });
test("xx", () => {
console.log(mockFn.mock.calls); // 목 함수에 '.mock'가 있고 그 안에 'calls'
expect("xx").toBe("xx");
});
test("함수는 4번 호출된다.", () => {
expect(mockFn.mock.calls.length).toBe(4);
});
test("2번째로 호출된 함수에 전달된 첫번째 인수는 1이다.", () => {
expect(mockFn.mock.calls[1][0]).toBe(1);
});
console.log
[ [], [ 1 ], [ 'a' ], [ [ 1, 2 ], { a: 'b' } ] ]
at Object.log (mock.test.js:11:11)
PASS ./mock.test.js
✓ xx (20 ms)
✓ 함수는 4번 호출된다. (1 ms)
✓ 2번째로 호출된 함수에 전달된 첫번째 인수는 1이다. (1 ms)
example) 제공된 배열의 각 항목에 대한 콜백을 호출하는 forEach 함수의 구현을 테스트한다고 가정한다.
const mockFn = jest.fn();
function forEachAdd(arr) {
arr.forEach((num) => {
mockFn(num + 1);
});
}
forEachAdd([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);
});
results
: 리턴된 값이 배열로 들어있음const mockFn = jest.fn((num) => num + 1);
mockFn(10);
mockFn(20);
mockFn(30);
test("함수 호출은 3번 된다.", () => {
console.log(mockFn.mock.results);
expect(mockFn.mock.calls.length).toBe(3);
});
// 테스트 코드
test("반환된 값은 11, 21, 31", () => {
expect(mockFn.mock.results[0].value).toBe(11);
expect(mockFn.mock.results[1].value).toBe(21);
expect(mockFn.mock.results[2].value).toBe(31);
});
console.log
[
{ type: 'return', value: 11 },
{ type: 'return', value: 21 },
{ type: 'return', value: 31 }
]
at Object.log (mock.test.js:48:11)
PASS ./mock.test.js
✓ 함수 호출은 3번 된다. (22 ms)
✓ 반환된 값은 11, 21, 31
mockReturnValue(리턴 값)
: 가짜 함수가 어떤 값을 리턴할지 설정const mockFn = jest.fn();
mockFn
.mockReturnValueOnce(true) //중간에 리턴값 변경
.mockReturnValueOnce(false)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
.mockReturnValue(true);
// [1, 2, 3, 4, 5].filter((num) => callback(num));
// callback 함수가 홀/짝을 판별해주는 역할을 해야 하는데, 당장 코드를 만들 수 없다면 목 함수 사용
const result = [1, 2, 3, 4, 5].filter((num) => mockFn(num));
test("홀수는 1,3,5", () => {
expect(result).toStrictEqual([1, 3, 5]); // 배열을 확인할 때는 toStrictEqual(O), toBe(X)
});
mockResolvedValue(Promise가 resolve하는 값)
: 가짜 비동기 함수 생성.then
을 이용해서 .mockResolvedValue()
에서 리턴해주는 값을 비교const mockFn = jest.fn();
mockFn.mockResolvedValue({ name: "FuBao" });
test("받아온 이름은 FuBao", () => {
mockFn().then((res) => {
expect(res.name).toBe("FuBao");
});
});
mockImplementation(구현 코드)
: 해당 함수를 통째로 즉석해서 재구현mocking은 단위 테스트를 작성할 때, 해당 코드가 의존하는 부분을 mock(모조품)로 대체하는 기법이다.
example) 외부 코드를 활용한 테스트를 진행한다고 가정한다.
// fn.js
const fn = {
createUser: (name) => { //실제 user DB에 user를 생성해주는 함수라고 가정
console.log("사용자가 생성되었습니다.");
return { name };
},
};
//fn.test.js
const fn = require("./fn");
// DB에 user가 생기지 않도록
jest.mock("./fn"); // `jesk.mock()`으로 fn을 mocking module로 만들어 줌
fn.createUser.mockReturnValue({ name: "FuBao" }); //{ name: "FuBao" }객체를 반환해주는 목 함수만 동작
test("user를 만든다.", () => {
const user = fn.createUser("FuBao"); // 호출되지 않음
expect(user.name).toBe("FuBao");
});
PASS ./module.test.js
✓ user를 만든다. (3 ms)
//fn.test.js
const fn = require("./fn");
// jest.mock("./fn");
// fn.createUser.mockReturnValue({ name: "FuBao" });
test("user를 만든다.", () => {
const user = fn.createUser("FuBao"); // 호출 됨.
expect(user.name).toBe("FuBao");
});
console.log
사용자가 생성되었습니다.
at Object.log [as createUser] (fn.js:44:13)
PASS ./module.test.js
✓ user를 만든다. (21 ms)
mocking에는 스파이(spy)라는 개념이 있다. 현실이나 영화 속에서 스파이라는 직업은 “몰래” 정보를 캐내야 한다. 테스트를 작성할 때도 이처럼, 어떤 객체에 속한 함수의 구현을 가짜로 대체하지 않고, 해당 함수의 호출 여부와 어떻게 호출되었는지만을 알아내야 할 때 jest.spyOn(object, methodName)
함수를 이용한다.
const calculator = {
add: (a, b) => a + b,
};
const spyFn = jest.spyOn(calculator, "add");
const result = calculator.add(2, 3);
expect(spyFn).toBeCalledTimes(1);
expect(spyFn).toBeCalledWith(2, 3);
expect(result).toBe(5);
toBeCalled()
: 한번 이상 호출toBeCalledTimes()
: 정확한 호출 횟수toBeCalledWith()
: 인수로 어떤 값들을 받았는지 확인lastCalledWith()
: 마지막으로 실행된 함수의 인수만 확인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);
// expect(mockFn).lastCalledWith(10, 20); -> faild test
});
참고문헌,
https://jestjs.io/docs/mock-functions,
[Youtube]Jest 강좌 - 코딩앙마,
https://www.daleseo.com/jest-fn-spy-on/,