테스트 하기 위해 흉내만 내는 함수
즉, 실제 함수를 구현한 것이 아니라 그저 흉내만 낸, 테스트 하기 위해서 만든 일종의 모형
mock
의 사전적 의미
모의의, 가짜의
mock up
의 사전적 의미
모형, 모의
어떤 작업을 테스트 할 때, 목 함수로 간단히 테스트 하는 것이 실제로 구현하는 것보다 나을 때
예시
user DB에 접근해서 user list를 select해오는 작업할 때, 작성해야 할 코드가 너무 많은 경우
만들어져 있는 코드가 있다면
→ 상관 無
but, 새로 만든다면
jest.fn()
으로 제작
호출 및 인수 넘기기 가능
mock
프로퍼티
호출 되었던 값들이 저장됨
내부
calls
배열
함수 호출된 횟수, 호출될 때 전달된 인수 파악 가능
fn.test.js
const mockFn = jest.fn(); // ✅ jest.fn()으로 목 함수 제작
// ✅ 호출
mockFn();
mockFn(1); // ✅ 인수 넘기기
// 의미 없는 테스트 작성
test("dd", () => {
console.log(mockFn.mock.calls) // 👈 목 함수에 'mock' 프로퍼티가 있고 그 안에 있는 calls
expect("dd").toBe("dd");
});
테스트 결과
> jest@1.0.0 test
> jest
console.log
[ [], [ 1 ] ]
# 👆 배열 내부에 전달된 인수의 값이 배열로 존재.
at Object.<anonymous> (fn.test.js:7:11)
PASS ./fn.test.js
✓ dd (10 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.124 s, estimated 1 s
fn.test.js
(...생략)
test("함수는 2번 호출됩니다.", () => {
expect(mockFn.mock.calls.length).toBe(2); // 👈 calls.length는 호출된 횟수
});
test("2번째로 호출된 함수의 첫 번째 인수는 1입니다.", () => {
expect(mockFn.mock.calls[1][0]).toBe(1);
});
테스트 결과
모두 통과
콜백 함수를 새로 만들지 않아도 목 함수를 이용해서 해당 함수가 잘 동작 하는지 파악 가능
배열(숫자)을 반복하면서 +1 한 값을 콜백 함수에 전달해주는 forEachAdd1
함수
fn.test.js
function forEachAdd1(arr) {
arr.forEach((num) => {
// fn(num+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입니다.", () => { // 전달된 값은 1씩 증가한 값
expect(mockFn.mock.calls[0][0]).toBe(11);
expect(mockFn.mock.calls[1][0]).toBe(21);
expect(mockFn.mock.calls[2][0]).toBe(31);
});
테스트 결과
모두 통과
어떤 값을 리턴(숫자 받아서 +1)하는 함수 제작
const mockFn = jest.fn((num) => num + 1);
// 1. 숫자를 받아 +1
// 2. jest.fn의 첫 번째 인수로 함수 전달
// 3. 호출
mockFn(10);
mockFn(20);
mockFn(30);
// 3번 호출
test("함수 호출은 3번 됩니다.", () => {
console.log(mockFn.mock.results); // 👈 mock.results 확인할 것
expect(mockFn.mock.calls.length).toBe(3);
});
results
에는return
된 값이 배열로 들어옴.
seonmijung@Galaxy-A7 Jest % npm test
> jest@1.0.0 test
> jest
console.log
[
{ type: 'return', value: 11 }, # 👈 1씩 증가된 값이 return
{ type: 'return', value: 21 },
{ type: 'return', value: 31 }
]
at Object.<anonymous> (fn.test.js:8:11)
PASS ./fn.test.js
✓ 함수 호출은 3번 됩니다. (11 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.125 s, estimated 1 s
Ran all test suites.
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("30에서 1 증가한 값이 반환된다.", () => {
expect(mockFn.mock.results[2].value).toBe(31);
});
1번째 실행된 것은 results[0]
에 들어갈 거고 2 번째, 3 번째 호출한 것은 1과 2에 들어감.
테스트는 모두 통과.
실행할 때 마다 각각 다른 값을 return 가능하게 함.
fn.test.js
const mockFn = jest.fn();
mockFn
.mockReturnValueOnce(10) // 👈 중간에 return 값을 바꾸려면 once 붙임
.mockReturnValueOnce(20)
.mockReturnValueOnce(30)
.mockReturnValue(40); // 👈 마지막에는 once 뺌.
mockFn();
mockFn();
mockFn();
mockFn();
test("dd", () => {
console.log(mockFn.mock.results); // 👈 확인
expect("dd").toBe("dd");
});
seonmijung@Galaxy-A7 Jest % npm test
> jest@1.0.0 test
> jest
console.log
[
{ type: 'return', value: 10 }, # 👈 10, 20, 30, 40이 리턴 된 것 확인
{ type: 'return', value: 20 },
{ type: 'return', value: 30 },
{ type: 'return', value: 40 }
]
at Object.<anonymous> (fn.test.js:15:11)
PASS ./fn.test.js
✓ dd (11 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.129 s, estimated 1 s
Ran all test suites.
1 ~ 5까지 받아서 홀수만 리턴하는 함수
fn.test.js
콜백 함수 작성
const mockFn = jest.fn();
[1, 2, 3, 4, 5].filter((num) => callback(num)); // 👈 callback
callback
함수가 홀/짝을 판별해주는 역할을 해야 되는데 지금 당장 코드를 만들 형편이 되지 않는다면 → 목 함수를 사용
const mockFn = jest.fn();
[1, 2, 3, 4, 5].filter((num) => mockFn(num)); // 👈 이렇게 사용
목 함수는 true
와 false
를 번갈아 가면서 리턴
const mockFn = jest.fn();
mockFn
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
.mockReturnValue(true);
let result = [1, 2, 3, 4, 5].filter((num) => mockFn(num)); // 👈 mockFn 부분 💥
test("홀수는 1, 3, 5", () => {
expect(result).toStrictEqual([1, 3, 5]); // 배열을 확인할 때는 toStrictEqual(O), toBe(X)
});
원래는 위 mockFn 부분 💥
에 홀짝 판별이 가능한 함수를 작성해야하는데 일단 목 함수를 사용하고 어떤 숫자가 넘어 오던지 순서대로 true
, false
를 번갈아 가면서 return을 해주고 있음.
실행을 해보면 1, 3, 5만 (num) => mockFn(num)
부분이 true일 것
→ 그래서 result
는 1, 3, 5
이제 목 함수 대신 실제 코드를 작성하면 됨.통과
비동기 함수 처럼 사용 가능
(then
을 이용해서.mockResolvedValue();
에서 return해주는 값을 비교 가능)
예시
fn.test.js
const mockFn = jest.fn();
mockFn.mockResolvedValue({ name: "Mike" }); // 👈
test("받아온 이름은 Mike", () => {
mockFn().then((res) => { // 👈 then 이용
expect(res.name).toBe("Mike");
});
});
then
을 이용해서 mockFn.mockResolvedValue({ name: "Mike" });
에서 return해주는 값을 비교jest.mock()
을 사용해서 mocking module
로 만듦
mockReturnValue()
등으로 return할 값 설정 가능
효과
실제 코드는 실행되지 않고 목 함수가 동작 (설정한 리턴 값을 반환)
외부 코드(fn.js
)를 활용한 테스트가 필요하다고 가정
user를 생성하는 함수를 테스트 해보고 싶은데 테스트 할 때마다 user가 생겨버리면 곤란. 그렇다고 테스트가 끝날 때마다 롤백해주는 것도 번거로움.
→ 이럴 때 mocking module
사용
번거로움 (테스트 할 때마다 user가 생겨서 곤란. 끝날 때마다 롤백 필요.)
외부 코드 (fn.js
)
이름을 받아서 user객체를 리턴해주는 함수 제작
실제로는 userDB에 user를 생성해주는 함수라고 가정
const fn = {
(...생략)
createUser: (name) => { // 👈
console.log("실제로 사용자가 생성 되었습니다.");
return {
name,
};
},
(...생략)
};
module.exports = fn;
외부 코드 사용 (fn.test.js
)
const fn = require("./fn");
test("유저를 만든다", () => {
const user = fn.createUser("Mike");
expect(user.name).toBe("Mike");
});
테스트 결과
통과. → 이런 경우 다시 DB로 접속해서 방금 만든 test user는 삭제 필요.
seonmijung@Galaxy-A7 Jest % npm test
> jest@1.0.0 test
> jest
console.log
실제로 사용자가 생성 되었습니다. # 👈 실제로 user가 생겨버림
at Object.createUser (fn.js:4:13)
PASS ./fn.test.js
✓ 유저를 만든다 (10 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.133 s, estimated 1 s
Ran all test suites.
fn.test.js
const fn = require("./fn");
jest.mock("./fn") // 👈 jest.mock()으로 fn을 mocking module로 만듦
fn.createUser.mockReturnValue({ name: "Mike" });
/* 👆
fn의 createUser를 사용할텐데 mockReturnValue를 통해서
{ name: "Mike" } 객체를 return */
test("유저를 만든다", () => {
(...생략)
});
실제 fn.createUser
는 호출 x.
{ name: "Mike" }
를 반환해주는 목 함수가 동작
테스트 결과
통과
아까 보였던 로그(실제로 사용자가 생성 되었습니다.
)는 안 찍힘. → 실제 user 생성 안됨.
toBeCalled
한 번이라도 호출 됬으면 통과
toBeCalledTimes
정확한 호출 횟수
toBeCalledWith
인수로 어떤 값을 받았는지 체크
lastCalledWith
마지막으로 실행된 인수만 체크
fn.test.js
const mockFn = jest.fn();
mockFn(10, 20);
mockFn();
mockFn(30, 40);
test("한 번 이상 호출?", () => {
expect(mockFn).toBeCalled();
});
test("정확히 3번 호출?", () => {
expect(mockFn).toBeCalledTimes(3); // 3번 호출되었기 때문에 → 3
});
test("10과 20을 전달 받은 함수가 있는가?", () => {
expect(mockFn).toBeCalledWith(10, 20);
});
test("마지막 함수는 30과 40을 받았음?", () => {
expect(mockFn).lastCalledWith(30, 40);
});
테스트 결과
모두 통과
toBeCalledWith
의 인수를 30, 40으로 바꾸어도 → 통과
lastCalledWith
의 인수를 10, 20으로 바꾸면 → 실패.
마지막 mockFn
은 30, 40을 전달했기 때문
참고