Web/JEST

JEST - Mock

dev_sr 2023. 12. 6. 23:37

Mock 함수는 새로운 함수를 구현한 게 아니라 테스트를 위해 구현하는 가짜함수이다.

 

가짜로 대체하는 이유

-테스트 하고 싶은 기능이 다른 기능과 엮여있을 경우 정확한 테스트를 하기 힘들다.

-내가 작성한 코드의 문제인지 api의 문제인지 db의 문제인지 다른 문제인지 파악하기 힘들다.

=>실제로 데이터베이스에 데이터를 넣지 않고 잘 작동한다는 전제하에 테스트를 하기 위해 mock이 필요하다

 

1. calls (호출확인)

Jest는 에서는 가짜 mock 함수를 생성할 수 있는 jest.fn()을 제공한다.

mock 함수에는 mock이라는 property가 있으며 이 안에는 calls라는 배열이 있다.

mock property에는 호출되었던 값들이 저장된다.

calls를 이용하면 함수가 총 몇번 호출 되었는 지, 호출될때 전달된 인수가 무엇인지 확인할 수 있다. 

 

const mockFn = jest.fn();

mockFn();
mockFn(1);

describe("mock test", () => {
  test("mock.calls", () => {
    console.log(mockFn.mock.calls);
    expect(1).toBe(1);
  });
  test("호출된 횟수", () => {
    expect(mockFn.mock.calls.length).toBe(2);
  });
  test("두번째로 전달된 인수는 1", () => {
    expect(mockFn.mock.calls[1][0]).toBe(1);
  });
});

 

 

 

mock 함수를 이용하면 실제 함수를 작성하지 않고 테스트를 먼저 진행해 볼 수 있다.

const mockFn = jest.fn();

function arrForEach(arr) {
  arr.forEach((num) => mockFn(num + 1));
}

arrForEach([1, 2, 3]);

describe("mock test", () => {
  test("호출된 횟수", () => {
    expect(mockFn.mock.calls.length).toBe(3);
  });
  test("두번째 호출된 값은 3", () => {
    expect(mockFn.mock.calls[1][0]).toBe(3);
  });
});

 

 

1.1. 호출 관련 matcher

toBeCalled = toHaveBeenCalled 호출됐나

toBeCalledTimes(times)toHaveBeenCalledTimes(times) 몇번 호출됐나 

const mockFn = jest.fn();

test("호출 됨?", () => {
  //아직 호출 안 됨?
  expect(mockFn).not.toBeCalled();

  mockFn();

  expect(mockFn).toBeCalled();

  mockFn();

  expect(mockFn).toBeCalledTimes(2);
});

 

 

toBeCalledWith 특정 인수를 받았는 지 

lastCalledWith 마지막 인수로 특정 인수로 받았는 지

const mockFn = jest.fn();

mockFn(1);
mockFn(3);

test("인수를 뭘로 받음?", () => {
  //1을 인수로 받았는 지?
  expect(mockFn).toBeCalledWith(1);
  //마지막 인수는 3으로 받았는 지?
  expect(mockFn).lastCalledWith(3);
});

 

 

2. mockImplemetation

모의 함수의 동작을 임의로 구현하는 함수이다.  실제 원본 함수의 기능을 대체할 수 있다.

//원본 함수
function add(a,b){
  retrun a + b;
}

//모의 함수
const mockFn = jest.fn();
mockFn.mockImplementation((a, b) => a + b);

mockFn(1, 2); //3

 

더 편하게 줄여쓸 수도 있다

const mockFn = jest.fn((a, b) => a + b);

mockFn(1, 2); //3

 

 

3. results(결과확인)

모의 함수가 어떻게 동작해야하는 지 정의하고 호출된 후 반환된 결과를 확인할 때 사용한다.

const mockFn = jest.fn((a, b) => a + b);

mockFn(1, 2);

test("returnValue", () => {
  console.log(mockFn.mock.results);
  expect(mockFn.mock.results[0].value).toBe(3);
});

 

 

 

4.  mockReturnValue / mockReturnValueOnce

mockReturnValue 는 모의 함수가 호출될 때마다 지정된 값을 반환하도록 설정한다.

const mockFn = jest.fn((a, b) => a + b);

mockFn.mockReturnValue(100);
mockFn(1, 2);

test("returnValue", () => {
  //fail, value는 100임
  expect(mockFn.mock.results[0].value).toBe(3); 
});

 

여러번 호출해도 항상 동일한 값이 나온다.

const mockFn = jest.fn();

mockFn.mockReturnValue(100);
mockFn();
mockFn(1);
mockFn(100);

 

 

 

mockReturnValueOnce는 모의 함수가 처음 호출될 때 지정된 값을 반환하고

다음 호출에는 원래 함수의 동작에 따라 값을 반환한다.

 

const mockFn = jest.fn((num) => num + 1);

mockFn.mockReturnValueOnce(100);
mockFn(1);
mockFn(1);
mockFn(100);

 

 

const mockFn = jest.fn((num) => num + 1);

mockFn.mockReturnValueOnce(100).mockReturnValue(200);
mockFn(1);
mockFn(1);
mockFn(100);

 

 

 

5. mockResolvedValue

mockResolvedValuepromise를 반환하는 비동기 함수의 결과값을 설정할 수 있다.

const mockFn = jest.fn();

mockFn.mockResolvedValue({ name: "감자" });

test("비동기 테스트", () => {
  mockFn().then((res) => {
    expect(res.name).toBe("감자");
  });
});

 

 

6. mocking module

api 연동 작업 시 실제로 db에 값을 생성했다가 지우기 보단 mocking module(모의화된 모듈)을 써서 테스트를 진행할 수 있다.

 

먼저, mocking module을 사용하지 않는 경우,

fn에 유저를 생성해주는 함수가 있다고 가정하고

createUser: (name) => {
    console.log("userDB에 사용자를 생성해주는 함수.");
    return {
      name,
    };
 },

 

테스트를 하면

const fn = require("./fn");

test("유저 생성", () => {
  const user = fn.createUser("감자");
  expect(user.name).toBe("감자");
});

 

 

실제로 userDB에 사용자를 생성해주는 함수가 실행돼서 console.log 메세지가 찍혔다.

 

 

실제로 db에 유저정보를 생성하지 않을려면 모듈을 모의화(mocking)해서 테스트를 진행할 수 있다.

모의화를 하면 실제 createUser 함수는 호출되지 않는다.

const fn = require("./fn");

jest.mock("./fn");
fn.createUser.mockReturnValue({ name: "감자" });

test("유저 생성", () => {
  const user = fn.createUser("감자");
  expect(user.name).toBe("감자");
});

 

 

 

 

 

 

참고

https://jestjs.io/docs/mock-function-api#jestmockt

https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-%EB%AA%A8%ED%82%B9-mocking-jestfn-jestspyOn

https://www.youtube.com/watch?v=9xBjErtlr1o&list=RDCMUCxft4RZ8lrK_BdPNz8NOP7Q&index=3

'Web > JEST' 카테고리의 다른 글

JEST - snapshot  (1) 2023.12.07
JEST - before, after, only, skip  (0) 2023.12.05
JEST - Matcher  (2) 2023.12.05