모킹
모킹 (Mocking)
테스트를 작성하다 보면 내부든 외부든 어떤 서비스를 '가짜' 버전으로 만들어야 하는 때가 반드시 와요. 이걸 흔히 모킹(mocking) 이라고 불러요. Vitest 는 vi 헬퍼로 이 일을 도와주는 유틸 함수들을 제공해요. vitest 에서 import 하거나, global 설정을 켰다면 전역으로 접근할 수도 있어요. 이 페이지는 vi.spyOn·vi.mock·vi.stubGlobal 같은 도구를 익숙하지 않은 사람을 위한 입문이자, 자주 쓰는 패턴을 모아 둔 치트시트 역할을 해요.
출처: Vitest — Mocking
::: tip
모킹이 처음이라면 Mock Functions 튜토리얼부터 시작해 보세요. vi.fn, vi.spyOn, vi.mock 을 직접 따라 하며 배울 수 있어요.
:::
::: warning
각 테스트 실행 전이나 후에 목을 반드시 clear 하거나 restore 해서 실행 사이의 목 상태 변경을 되돌리는 걸 잊지 마세요! 자세한 내용은 mockReset 문서를 참고하세요.
:::
vi.fn, vi.mock, vi.spyOn 메서드가 익숙하지 않다면 먼저 API 섹션을 확인하세요.
Vitest 는 모킹에 관한 방대한 가이드 목록을 준비해 뒀어요:
- Mocking Classes
- Mocking Dates
- Mocking the File System
- Mocking Functions
- Mocking Globals
- Mocking Modules
- Mocking Requests
- Mocking Timers
모킹을 더 빠르고 간단하게 시작하고 싶다면 아래 치트시트를 보세요.
치트시트
"나는 ~을(를) 모킹하고 싶다"에서 시작해서 패턴을 골라 쓰면 돼요.
export 된 변수 모킹하기
export const getter = 'variable'
import * as exports from './example.js'
vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked')
::: warning 이 방법은 Browser Mode 에서 동작하지 않아요. 해결책이 필요하면 Limitations 를 참고하세요. :::
export 된 함수 모킹하기
vi.mock을 쓰는 예시:
::: warning
vi.mock 호출은 파일 상단으로 호이스팅된다는 걸 잊지 마세요. 모든 import 보다 항상 먼저 실행돼요.
:::
export function method() {}
import { method } from './example.js'
vi.mock('./example.js', () => ({
method: vi.fn()
}))
vi.spyOn을 쓰는 예시:
import * as exports from './example.js'
vi.spyOn(exports, 'method').mockImplementation(() => {})
::: warning
vi.spyOn 예시는 Browser Mode 에서 동작하지 않아요. 해결책은 Limitations 를 보세요.
:::
export 된 클래스 구현 모킹하기
- 가짜
class를 쓰는 예시:
export class SomeClass {}
import { SomeClass } from './example.js'
vi.mock(import('./example.js'), () => {
const SomeClass = vi.fn(class FakeClass {
someMethod = vi.fn()
})
return { SomeClass }
})
vi.spyOn을 쓰는 예시:
import * as mod from './example.js'
vi.spyOn(mod, 'SomeClass').mockImplementation(class FakeClass {
someMethod = vi.fn()
})
::: warning
vi.spyOn 예시는 Browser Mode 에서 동작하지 않아요. 해결책은 Limitations 를 보세요.
:::
함수가 반환한 객체 감시하기
- 캐시를 이용하는 예시:
export function useObject() {
return { method: () => true }
}
import { useObject } from './example.js'
const obj = useObject()
obj.method()
import { useObject } from './example.js'
vi.mock(import('./example.js'), () => {
let _cache
const useObject = () => {
if (!_cache) {
_cache = {
method: vi.fn(),
}
}
// now every time that useObject() is called it will
// return the same object reference
return _cache
}
return { useObject }
})
const obj = useObject()
// obj.method was called inside some-path
expect(obj.method).toHaveBeenCalled()
모듈 일부만 모킹하기
import { mocked, original } from './some-path.js'
vi.mock(import('./some-path.js'), async (importOriginal) => {
const mod = await importOriginal()
return {
...mod,
mocked: vi.fn()
}
})
original() // has original behaviour
mocked() // is a spy function
::: warning
이건 _외부 접근_만 모킹한다는 걸 잊지 마세요(Mocking Modules pitfalls 참고). 이 예시에서 original 이 내부에서 mocked 를 호출한다면, 모킹 팩토리 말고 항상 모듈 안에 정의된 함수를 호출해요.
:::
현재 날짜 모킹하기
Date 와 Temporal 의 시간을 모킹하려면 vi.setSystemTime 헬퍼를 쓰면 돼요. 이 값은 다른 테스트 사이에 자동으로 리셋되지 않아요.
vi.useFakeTimers 를 쓰는 것도 Date 의 시간을 바꾼다는 점을 주의하세요.
const mockDate = new Date(2022, 0, 1)
vi.setSystemTime(mockDate)
const now = new Date()
expect(now.valueOf()).toBe(mockDate.valueOf())
const nowInstant = Temporal.Now.instant()
expect(nowInstant.epochMilliseconds).toBe(mockDate.valueOf())
// reset mocked time
vi.useRealTimers()
전역 변수 모킹하기
globalThis 에 값을 할당하거나 vi.stubGlobal 헬퍼를 써서 전역 변수를 설정할 수 있어요. vi.stubGlobal 을 쓸 때는 unstubGlobals 설정 옵션을 켜거나 vi.unstubAllGlobals 를 호출하지 않으면 다른 테스트 사이에 자동 리셋되지 않아요.
vi.stubGlobal('__VERSION__', '1.0.0')
expect(__VERSION__).toBe('1.0.0')
import.meta.env 모킹하기
- 환경 변수를 바꾸려면 그냥 새 값을 할당하면 돼요.
::: warning 환경 변수 값은 다른 테스트 사이에 자동으로 리셋되지 않아요. :::
import { beforeEach, expect, it } from 'vitest'
// you can reset it in beforeEach hook manually
const originalViteEnv = import.meta.env.VITE_ENV
beforeEach(() => {
import.meta.env.VITE_ENV = originalViteEnv
})
it('changes value', () => {
import.meta.env.VITE_ENV = 'staging'
expect(import.meta.env.VITE_ENV).toBe('staging')
})
- 값을 자동으로 리셋하고 싶다면
unstubEnvs설정 옵션을 켠 상태로vi.stubEnv헬퍼를 쓰면 됩니다(beforeEach훅에서vi.unstubAllEnvs를 직접 호출해도 되고요):
import { expect, it, vi } from 'vitest'
// before running tests "VITE_ENV" is "test"
import.meta.env.VITE_ENV === 'test'
it('changes value', () => {
vi.stubEnv('VITE_ENV', 'staging')
expect(import.meta.env.VITE_ENV).toBe('staging')
})
it('the value is restored before running an other test', () => {
expect(import.meta.env.VITE_ENV).toBe('test')
})
export default defineConfig({
test: {
unstubEnvs: true,
},
})
더 알아보기 (Learn more)
- Mock Functions —
vi.fn·vi.spyOn·vi.mock을 직접 따라 하며 배우는 튜토리얼 - vi API —
vi객체의 전체 API - Mocking Modules — 모듈 모킹의 함정과 심화
- Conditional Mocking —
vi.when으로 조건부 모킹