> [!date] published: 2025-06-06 - [The Jest Object · Jest](https://jestjs.io/docs/jest-object) - [Manual Mocks · Jest](https://jestjs.io/docs/manual-mocks) # Jest Mock Jest에서 mock을 다룰 때는 새 mock 함수를 만들 것인지, 모듈 전체를 대체할 것인지, 기존 메서드를 감시할 것인지를 먼저 구분한다. ## `jest.fn(implementation?)` > 새로운 mock 함수 생성 선택적으로 내부 구현을 함께 전달할 수 있다. 기본적으로는 `undefined`를 반환함. ```ts const mockFn = jest.fn(); mockFn(); expect(mockFn).toHaveBeenCalled(); // With a mock implementation: const returnsTrue = jest.fn(() => true); console.log(returnsTrue()); // true; ``` ## jest.mock(moduleName, factory, options) > 전체 모듈을 mock으로 대체하는 것 모듈 전체를 자동으로 모킹한다. import 시점에서부터 mock이 적용됨 <== 호이스팅되어 최상단에서 실행된다. 기본 사용 ```ts // banana.ts module.exports = () => "banana"; ``` ```ts jest.mock("../banana"); const banana = require("../banana"); // banana will be explicitly mocked. banana(); // will return 'undefined' because the function is auto-mocked. ``` 팩토리 인자와 함께 사용하기 ```ts jest.mock("../moduleName", () => { return jest.fn(() => 42); // 👈 }); // This runs the function specified as second argument to `jest.mock`. const moduleName = require("../moduleName"); moduleName(); // Will return '42'; ``` ES6의 default export를 사용하는 모듈일 때: `__esModule: true` 프로퍼티가 필요하다. ```ts import moduleName, { foo } from "../moduleName"; jest.mock("../moduleName", () => { return { __esModule: true, default: jest.fn(() => 42), foo: jest.fn(() => 43), }; }); moduleName(); // Will return 42 foo(); // Will return 43 ``` 모듈의 일부분만 모킹해야 할 때 ```ts jest.mock("./mathUtils", () => ({ ...jest.requireActual("./mathUtils"), // 실제 구현 유지 complexCalculation: jest.fn(() => "mocked result"), // 특정 함수만 Mock })); ``` ## jest.spyOn(object, methodName) > 기존 메서드를 감시하면서 mock 기능을 추가하는 것 기본적으로는 원본 구현을 유지한다. 호출 여부를 검증하면서 필요할 때만 구현을 대체할 수 있다. 감시만 할 때: ```ts const calculator = { add: (a, b) => a + b, subtract: (a, b) => a - b, }; describe("spyOn 기본 감시", () => { test("원본 메서드 호출 감시", () => { const addSpy = jest.spyOn(calculator, "add"); const result = calculator.add(2, 3); expect(result).toBe(5); // 원본 메서드가 실행됨 expect(addSpy).toHaveBeenCalledWith(2, 3); expect(addSpy).toHaveBeenCalledTimes(1); addSpy.mockRestore(); // 원본 복원 }); }); ``` 모킹할 때: ```ts describe("spyOn 구현 대체", () => { let consoleSpy; beforeEach(() => { consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {}); }); afterEach(() => { consoleSpy.mockRestore(); // 원본 console.log 복원 }); test("console.log 출력 제어", () => { console.log("이 메시지는 출력되지 않음"); expect(consoleSpy).toHaveBeenCalledWith("이 메시지는 출력되지 않음"); }); }); ``` ## fn vs spyOn | 구분 | `jest.fn()` | `jest.spyOn()` | | ---------------- | ---------------------------- | ---------------------------------- | | 목적 | 새로운 mock 함수를 생성할 때 | 기존 메서드를 감시하거나 대체할 때 | | 원본 구현 | 없음 (완전히 새로 구현한다) | 기본적으로는 유지된다 | | 복원 | 불가능 (복원할 상태가 없다) | `mockRestore()` | | 언제 사용하는가? | 의존성을 완전히 제거할 때 | 메서드 호출을 감시할 때 | > [!note] 개인적인 선택 기준 > > - 외부 라이브러리의 경우 : jest.fn() > - 내부 함수의 경우 : jest.spyOn() 함수의 내부 구현이 어떤 쪽에서든 의미가 있다면 `jest.spyOn`을 쓰게 되는 것 같다. ## jest.mock vs spyOn `jest.mock()`은 모듈 전체 또는 특정 export의 반환값을 테스트용으로 대체할 때 사용한다. `jest.spyOn()`은 기존 객체의 메서드 호출을 감시하거나, 그 메서드의 구현을 일시적으로 바꿀 때 사용한다. | 구분 | `jest.mock()` | `jest.spyOn()` | | --- | --- | --- | | 목적 | 모듈 또는 export를 테스트용 구현으로 대체 | 기존 객체의 메서드를 감시하거나 대체 | | 제어 범위 | import되는 모듈 단위 | 이미 존재하는 객체의 특정 메서드 | | 원본 구현 | 기본적으로 대체됨 | 기본적으로 유지됨 | | 반환값 제어 | 모듈 export의 반환값을 통째로 제어하기 좋음 | 특정 메서드의 반환값을 제어하기 좋음 | | 복원 | mock 해제나 모듈 리셋이 필요 | `mockRestore()` 사용 | 훅이나 모듈 export처럼 테스트 대상 코드가 import해서 사용하는 의존성의 반환값을 통째로 바꿔야 한다면 `jest.mock()`이 더 자연스럽다. 반대로 이미 가진 객체의 메서드가 호출되었는지 확인하거나, 원본 구현을 유지한 채 호출만 감시하고 싶다면 `jest.spyOn()`이 더 자연스럽다. ## mockImplementation() > mock 함수의 구현체를 직접 정의하기 ```ts const mockFn = jest.fn(); mockFn.mockImplementation(() => "mocked value"); expect(mockFn()).toBe("mocked value"); ``` ## mockReturnValue() > mock 함수의 반환값을 직접 정의하기 ```ts const mockFn = jest.fn(); mockFn.mockReturnValue("mocked value"); expect(mockFn()).toBe("mocked value"); ``` ## 관련 문서 - [[Jest Cleanup]] - [[Jest Matcher]] - [[React Testing Library 에러 해결 노트]]