Jest : Mock Functions 4️⃣

m_ngyeong·2023년 10월 31일
0
post-thumbnail

Jest

Mock Functions

테스트하기 위해 흉내만 내는 함수이다. 외부요인(네트워크 환경, 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)

forEach() Test

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);
});

Mock Return Values

  • 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 Modules

mocking은 단위 테스트를 작성할 때, 해당 코드가 의존하는 부분을 mock(모조품)로 대체하는 기법이다.

jset.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)

jest.spyOn()

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);

Matchers

  • 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/,

profile
ʚȉɞ

0개의 댓글