테스트 코드 도입 (jest mock이 뭘까)

박상하·2025년 8월 27일

출처 - 애니메이션 나루토 속 바꿔치기 술법

테스트 코드를 본격적으로 작성하며 알아야할 게 많이 있는데 그 중 먼저 기초가 되는
jest의 mocking에 대해 알아보자

Mocking 이란

mock 이란 모조품 즉, 가짜이다.
진짜를 가짜로 바꿔치기 후 테스트 수행할 때 필요한 기술

왜 mocking이 필요할까?

의존적이지 않은 별개의 테스트를 진행하기 위해서이다

만약 우리 서비스 중 로그인에 대한 단위테스트를 진행한다고 생각해보자.
그 중 login 후 사용자에게 ("환영합니다 !")라는 메세지가 잘 가는지에 대한 테스트를 생각해 볼 때, login에 필요한 api를 가져와 이를 기반으로 테스트를 하면 각 테스트는 하나 이상의 기능을 테스트 하는 것이고 또, 백엔드 네트워크 상황에 따라 의존성을 갖게 된다.

분명, 단위 테스트는 의존적이지 않고 특정 기능만을 테스트할 수 있어야 한다고 생각한다.

이에 위반되기에 mocking을 통해 원하는 기능만을 테스트할 수 있도록 한다.
즉, mocking을 사용하면 가짜와 진짜를 바꿔치기해서 원하는 기능만을 테스트할 수 있도록 한다. (의존성 다운)

jest.fn()

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

const mockFn = jest.fn()

console.log(mockFn(1))
console.log(mockFn("abc"))
console.log(mockFn([1,2]))
console.log(mockFn({a:"b"}))

// 모두 undefined를 return

mock 함수의 호출 결과는 모두 undefined이다.
default 값이고

mock.mockReturnValue(리턴값)
// 동기적 리턴 값 지정
mock.mockResolvedValue(리턴값)
// 비동기적 리턴 값 지정

이렇게 리턴 값들을 임의로 저장할 수 있다.

이런 것도 가능하다

mock.mockImplementation((a,b)=>a+b는 ${a+b} 입니다)

즉, 함수를 구현해서 넣어줄 수도 있다.

mock 함수가 유용한 이유

가짜 함수로 테스트를 진행할 수 있다는 것 외에 좋은 점은

가짜 함수는 자기 자신을 기억한다는 점이다.

이게 무슨 말이냐면 내가 몇 번 호출되었는지, 어떤 인자와 호출되었는지 등을
호출 이후 시점에서 알 수 있다는 점이다.

mockFn("안녕하세요");
mockFn("반갑습니다");
mockFn(6);

expect(mockFn).toHaveBeenCalledTimes(3)
expect(mockFn).toHaveBeenCalledWith("안녕하세요");
expect(mockFn).toHaveBeenCalledWith("반갑습니다");
expect(mockFn).toHaveBeenCalledWith(6);

특히 toHaveBeenCalledTimes Matcher를 통해
우리 서비스의 불필요한 API 호출 횟수를 줄일 수 있을 거라 기대한다.

Matcher 란?

기대하는 결과 값을 넣을 수 있는 메서드라 보면된다.

jest는 다양한 방법으로 테스트의 Values를 예측할 수 있다.

그 다양한 방법이 Matcher이다.

Jest 공식문서 - Matcher

대표적으로

  • toBe
  • toEqual
  • toBeTruthy
  • toBeUndefined
  • toBeFalsy
  • toBeGreaterThan
  • toBeLessThan

등등이 있고 레퍼런스를 보면 굉장히 많다.
동기적 결과값 뿐 아니라 비동기적 결과값도 지원하기 때문에 자유롭게 사용하면 된다.

jest.spyOn()

jest의 mocking 에는 spy(스파이) 라는 개념이 있다.
어떤 객체에 속한 함수의 구현을 가짜로 대체 하지 않고
해당 함수의 호출 여부와 어떻게 호출 되었는지 알아야할 때가 있다.

이럴 때 사용하는게 jest.spyOn(ObjectName, methodName)이다

객체 내부 함수를 트래킹할 때 사용할 수 있는 jest 함수 !!!

const calculator ={
 add : (a,b) =>  a+b
}

const spyFn = jest.spyOn(calculator,"add")
//methodName은 string 타입으로

const result = calculator.add(2,3)
// 1번 호출에 result는 5를 저장함

expect(spyFn).toHaveBeenCalledTimes(1)
expect(spyFn).toHaveBeenCalledWith(2,3)
//이를 spy함수를 통해 추적할 수 있음

import한 모듈의 기능을 테스트할 때 주의할 점

테스트 코드를 작성하다보면 다른 컴포넌트나 파일에서 불러온 함수를 사용하는 경우가 많다.


//./loginService.ts
const logout=()=>{
//어쩌고저쩌고
}
const login=()=>{
//어쩌고저쩌고
}

// userService.test.js
import {logout,login} from "./loginService";
// 아~ logout / login은 외부 모듈이니까 일단 목 함수로 변경해야지~

logout = jest.fn()
login = jest.fn()
// 이게 될까?

위 코드가 가능할까? ❌
const로 선언된 모듈이기 때문에 재할당이 불가능~~

만약 loginService가 객체로 이루어져있다면?

const loginService={
 login:()=>{},
 logout:()=>{}
}

// userService.test.js

const * as loginService from "./loginService";

loginService.login = jest.fn();
loginService.logout = jest.fn();

이건 가능하다. 그런데 이보다 더 좋은 방법이 있다.

jest.mock()을 이용해보자

이렇게 좀 까다로울 수 있는 모듈의 mock 변환을 좀 더 편하게 할 수 있게
jest.mock()이라는 메서드를 제공한다.

import {logout,login} from "./loginService";

jest.mock("./loginService")
// 이렇게 모듈 경로를 mock 메서드에 넣으면
// 해당 내부 모든 export 된 함수를 mock 화 한 뒤 사용이 가능하다.

logout()
// 이건 return 했을 때 undefined를 return 한다 왜? mock함수가 되었으니까


이렇게 모듈 자체 모든 함수를 mock 함수화 하여 모듈을 가져와
mock함수를 만들 때의 수고스러움을 덜어준다. 😀

참고 블로그

실제 테스트 실행

0개의 댓글