Skip to content
Vitest 0
Main Navigation 가이드API구성고급
1.6.1
0.34.6

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

외관

Sidebar Navigation

가이드

Vitest를 선택하는 이유

시작하기

기능

워크스페이스

명령줄 인터페이스

테스트 필터링

커버리지

스냅샷

모의화

타입 테스트

Vitest UI

브라우저 모드 (실험적)

소스 내 테스트

테스트 컨텍스트

테스트 환경

Matcher 확장하기

IDE 통합

디버깅

다른 테스트 러너와의 비교

마이그레이션 가이드

일반적인 오류

API

테스트 API 참조

Mock 함수

Vi

expect

expectTypeOf

assertType

구성

Vitest 설정하기

이 페이지에서

모의화 ​

테스트를 작성하다 보면 내부 또는 외부 서비스의 "가짜" 버전을 만들어야 할 때가 있습니다. 이를 일반적으로 **모의화(mocking)**라고 합니다. Vitest는 vi 도우미를 통해 이를 돕는 유틸리티 함수를 제공합니다. import { vi } from 'vitest'를 사용하여 가져오거나, 전역으로 접근할 수 있습니다 ( 전역 구성이 활성화된 경우).

WARNING

각 테스트 실행 전후에 모의화(mock)를 항상 초기화하거나 복원하여 테스트 간의 모의화 상태 변경을 방지해야 합니다! 자세한 내용은 mockReset 문서를 참조하십시오.

빠르게 시작하려면 API 섹션을 확인하고, 모의화에 대해 자세히 알아보세요.

날짜 ​

테스트 시 일관성을 유지하기 위해 날짜를 제어해야 할 경우가 있습니다. Vitest는 타이머와 시스템 날짜를 조작하기 위해 @sinonjs/fake-timers 패키지를 사용합니다. 특정 API에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

예시 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const businessHours = [9, 17];

function purchase() {
  const currentHour = new Date().getHours();
  const [open, close] = businessHours;

  if (currentHour > open && currentHour < close) return { message: 'Success' };

  return { message: 'Error' };
}

describe('purchasing flow', () => {
  beforeEach(() => {
    // vitest에게 모의 시간 사용을 알립니다.
    vi.useFakeTimers();
  });

  afterEach(() => {
    // 각 테스트 실행 후 날짜를 복원합니다.
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // 영업 시간 내로 시간을 설정합니다.
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // Date.now()에 접근하면 위에서 설정한 날짜가 반환됩니다.
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // 영업 시간 외로 시간을 설정합니다.
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // Date.now()에 접근하면 위에서 설정한 날짜가 반환됩니다.
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

함수 ​

함수 모의화는 스파이와 모의 함수, 두 가지 범주로 나눌 수 있습니다.

특정 함수가 호출되었는지 (그리고 어떤 인수가 전달되었는지) 확인하기만 하면 되는 경우가 있습니다. 이러한 경우 스파이만으로 충분하며, vi.spyOn()을 사용하여 직접 생성할 수 있습니다 (자세한 내용은 여기 참조).

그러나 스파이는 함수를 감시하는 데만 사용되며, 해당 함수의 구현을 변경할 수는 없습니다. 함수의 가짜 (또는 모의화된) 버전을 만들어야 하는 경우 vi.fn()을 사용할 수 있습니다 (자세한 내용은 여기 참조).

함수 모의화의 기반으로 Tinyspy를 사용하지만, jest와 호환성을 위해 자체 래퍼를 가지고 있습니다. vi.fn()과 vi.spyOn()은 동일한 메서드를 공유하지만, vi.fn()의 반환 값만 호출할 수 있습니다.

예시 ​

js
import { afterEach, describe, expect, it, vi } from 'vitest';

function getLatest(index = messages.items.length - 1) {
  return messages.items[index];
}

const messages = {
  items: [
    { message: 'Simple test message', from: 'Testman' },
    // ...
  ],
  getLatest, // getter 또는 setter도 지원됩니다.
};

describe('reading messages', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should get the latest message with a spy', () => {
    const spy = vi.spyOn(messages, 'getLatest');
    expect(spy.getMockName()).toEqual('getLatest');

    expect(messages.getLatest()).toEqual(
      messages.items[messages.items.length - 1]
    );

    expect(spy).toHaveBeenCalledTimes(1);

    spy.mockImplementationOnce(() => 'access-restricted');
    expect(messages.getLatest()).toEqual('access-restricted');

    expect(spy).toHaveBeenCalledTimes(2);
  });

  it('should get with a mock', () => {
    const mock = vi.fn().mockImplementation(getLatest);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(1);

    mock.mockImplementationOnce(() => 'access-restricted');
    expect(mock()).toEqual('access-restricted');

    expect(mock).toHaveBeenCalledTimes(2);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(3);
  });
});

더 보기 ​

  • Jest의 Mock Functions

전역 변수 ​

vi.stubGlobal 헬퍼를 사용하여 jsdom 또는 node에 없는 전역 변수를 모의화할 수 있습니다. 전역 변수의 값을 globalThis 객체에 할당합니다.

ts
import { vi } from 'vitest';

const IntersectionObserverMock = vi.fn(() => ({
  disconnect: vi.fn(),
  observe: vi.fn(),
  takeRecords: vi.fn(),
  unobserve: vi.fn(),
}));

vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);

// 이제 `IntersectionObserver` 또는 `window.IntersectionObserver`로 접근할 수 있습니다.

모듈 ​

모듈 모의화는 다른 코드에서 호출되는 타사 라이브러리를 감시하여 인수, 출력 또는 구현을 재정의할 수 있도록 합니다.

자세한 API 설명은 vi.mock() API 섹션을 참조하십시오.

자동 모의화 알고리즘 ​

코드에서 연결된 __mocks__ 파일이나 해당 모듈에 대한 factory 없이 모의화된 모듈을 가져오는 경우, Vitest는 모듈을 호출하고 모든 내보내기를 모의화하여 모듈 자체를 자동으로 모의화합니다.

다음 규칙이 적용됩니다.

  • 모든 배열은 비워집니다.
  • 모든 원시 타입 및 컬렉션은 동일하게 유지됩니다.
  • 모든 객체는 깊은 복사됩니다.
  • 클래스의 모든 인스턴스와 해당 프로토타입은 깊은 복사됩니다.

예시 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Client } from 'pg';
import { failure, success } from './handlers.js';

// handlers
export function success(data) {}
export function failure(data) {}

// get todos
export async function getTodos(event, context) {
  const client = new Client({
    // ...clientOptions
  });

  await client.connect();

  try {
    const result = await client.query('SELECT * FROM todos;');

    client.end();

    return success({
      message: `${result.rowCount} item(s) returned`,
      data: result.rows,
      status: true,
    });
  } catch (e) {
    console.error(e.stack);

    client.end();

    return failure({ message: e, status: false });
  }
}

vi.mock('pg', () => {
  const Client = vi.fn();
  Client.prototype.connect = vi.fn();
  Client.prototype.query = vi.fn();
  Client.prototype.end = vi.fn();

  return { Client };
});

vi.mock('./handlers.js', () => {
  return {
    success: vi.fn(),
    failure: vi.fn(),
  };
});

describe('get a list of todo items', () => {
  let client;

  beforeEach(() => {
    client = new Client();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('should return items successfully', async () => {
    client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);

    expect(success).toBeCalledWith({
      message: '0 item(s) returned',
      data: [],
      status: true,
    });
  });

  it('should throw an error', async () => {
    const mError = new Error('Unable to retrieve rows');
    client.query.mockRejectedValueOnce(mError);

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);
    expect(failure).toBeCalledWith({ message: mError, status: false });
  });
});

요청 ​

Vitest는 Node 환경에서 실행되므로 네트워크 요청을 모의화하기가 어려울 수 있습니다. 웹 API를 사용할 수 없으므로 네트워크 동작을 모방할 수 있는 도구가 필요합니다. 이러한 이유로 Mock Service Worker를 사용하는 것을 권장합니다. 이를 통해 REST 및 GraphQL 네트워크 요청을 모두 모의화할 수 있으며, 특정 프레임워크에 종속되지 않습니다.

Mock Service Worker (MSW)는 테스트 과정에서 발생하는 요청을 가로채 애플리케이션 코드 수정 없이 모의화 기능을 제공합니다. 브라우저에서는 Service Worker API를 사용하고, Node.js 및 Vitest 환경에서는 node-request-interceptor를 활용합니다. MSW에 대한 자세한 내용은 소개를 참조하십시오.

구성 ​

설정 파일에서 다음과 같이 설정하여 사용할 수 있습니다.

js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { graphql, rest } from 'msw';

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
];

export const restHandlers = [
  rest.get('https://rest-endpoint.example/path/to/posts', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(posts));
  }),
];

const graphqlHandlers = [
  graphql.query(
    'https://graphql-endpoint.example/api/v1/posts',
    (req, res, ctx) => {
      return res(ctx.data(posts));
    }
  ),
];

const server = setupServer(...restHandlers, ...graphqlHandlers);

// 모든 테스트 전에 서버를 시작합니다.
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// 모든 테스트 후에 서버를 닫습니다.
afterAll(() => server.close());

// 각 테스트 후 핸들러를 초기화합니다. (테스트 격리를 위해 중요합니다.)
afterEach(() => server.resetHandlers());

onUnhandleRequest: 'error' 설정을 통해 서버에 정의되지 않은 요청이 발생할 경우 오류를 발생시킬 수 있습니다.

예시 ​

MSW를 활용한 완전한 예제는 다음 링크에서 확인할 수 있습니다. MSW를 사용한 React 테스팅.

더 보기 ​

MSW에는 훨씬 더 많은 기능이 있습니다. 쿠키, 쿼리 매개변수 접근, 모의 오류 응답 정의 등 다양한 기능을 제공합니다! MSW로 할 수 있는 모든 것을 보려면 해당 문서를 참조하십시오.

타이머 ​

시간 제한 또는 간격이 포함된 코드를 테스트할 때, 테스트가 완료될 때까지 기다리거나 시간 초과를 발생시키는 대신, setTimeout 및 setInterval 호출을 모의화하는 '가짜' 타이머를 사용하여 테스트 속도를 향상시킬 수 있습니다.

자세한 API 설명은 vi.useFakeTimers API 섹션을 참조하십시오.

예시 ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

function executeAfterTwoHours(func) {
  setTimeout(func, 1000 * 60 * 60 * 2); // 2 hours
}

function executeEveryMinute(func) {
  setInterval(func, 1000 * 60); // 1 minute
}

const mock = vi.fn(() => console.log('executed'));

describe('delayed execution', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.restoreAllMocks();
  });
  it('should execute the function', () => {
    executeAfterTwoHours(mock);
    vi.runAllTimers();
    expect(mock).toHaveBeenCalledTimes(1);
  });
  it('should not execute the function', () => {
    executeAfterTwoHours(mock);
    // 2ms만큼 시간을 진행해도 함수가 실행되지 않습니다.
    vi.advanceTimersByTime(2);
    expect(mock).not.toHaveBeenCalled();
  });
  it('should execute every minute', () => {
    executeEveryMinute(mock);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(1);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(2);
  });
});

Cheat Sheet ​

INFO

아래 예제의 vi는 vitest에서 직접 가져온 것입니다. 구성에서 globals를 true로 설정하면 전역으로 사용할 수도 있습니다.

다음을 수행하고 싶습니다…

  • method 스파이하기
ts
const instance = new SomeClass();
vi.spyOn(instance, 'method');
  • 내보낸 변수 모킹하기
js
// some-path.js
export const getter = 'variable';
ts
// some-path.test.ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');
  • 내보낸 함수 모킹하기

vi.mock을 사용한 예시:

ts
// ./some-path.js
export function method() {}
ts
import { method } from './some-path.js';

vi.mock('./some-path.js', () => ({
  method: vi.fn(),
}));

WARNING

vi.mock 호출은 파일 상단으로 호이스팅되므로 beforeEach 내부에 넣지 마세요. beforeEach 내부에 둘 이상의 vi.mock 호출이 있을 경우, 그 중 하나만 모듈을 모킹합니다.

vi.spyOn을 사용한 예:

ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'method').mockImplementation(() => {});
  • 내보낸 클래스 구현 모킹하기

vi.mock 및 프로토타입을 사용한 예:

ts
// some-path.ts
export class SomeClass {}
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn();
  SomeClass.prototype.someMethod = vi.fn();
  return { SomeClass };
});
// SomeClass.mock.instances는 SomeClass의 인스턴스를 갖습니다.

vi.mock 및 반환 값을 사용한 예:

ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.returns는 반환된 객체들을 갖습니다.

vi.spyOn을 사용한 예:

ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // 처음 두 예제 중 원하는 방식으로 구현할 수 있습니다.
});
  • 함수에서 반환된 객체 스파이하기

캐시를 사용한 예:

ts
// some-path.ts
export function useObject() {
  return { method: () => true };
}
ts
// useObject.js
import { useObject } from './some-path.js';

const obj = useObject();
obj.method();
ts
// useObject.test.js
import { useObject } from './some-path.js';

vi.mock('./some-path.js', () => {
  let _cache;
  const useObject = () => {
    if (!_cache) {
      _cache = {
        method: vi.fn(),
      };
    }
    // 이제 useObject()가 호출될 때마다
    // 동일한 객체 레퍼런스를 반환합니다.
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method는 some-path 내부에서 호출되었습니다.
expect(obj.method).toHaveBeenCalled();
  • 모듈의 일부만 모킹하기
ts
import { mocked, original } from './some-path.js';

vi.mock('./some-path.js', async () => {
  const mod = await vi.importActual<typeof import('./some-path.js')>(
    './some-path.js'
  );
  return {
    ...mod,
    mocked: vi.fn(),
  };
});
original(); // 원본 동작을 유지합니다.
mocked(); // 스파이 함수로 모킹되었습니다.
  • 현재 날짜 모킹하기

Date의 시간을 모의하려면 vi.setSystemTime 도우미 함수를 사용할 수 있습니다. 이 값은 테스트 간에 자동으로 재설정되지 않습니다.

vi.useFakeTimers를 사용하면 Date의 시간도 변경된다는 점에 유의하세요.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// 모킹된 시간 초기화
vi.useRealTimers();
  • 전역 변수 모킹하기

globalThis에 값을 할당하거나 vi.stubGlobal 도우미를 사용하여 전역 변수를 설정할 수 있습니다. vi.stubGlobal을 사용하는 경우 unstubGlobals 구성 옵션을 활성화하거나 vi.unstubAllGlobals를 호출하지 않는 한 테스트 간에 자동으로 재설정되지 않습니다.

ts
vi.stubGlobal('__VERSION__', '1.0.0');
expect(__VERSION__).toBe('1.0.0');
  • import.meta.env 모킹하기

환경 변수를 변경하려면 새 값을 할당하기만 하면 됩니다. 이 값은 테스트 간에 자동으로 재설정되지 않습니다.

ts
import { beforeEach, expect, it } from 'vitest';

// beforeEach 훅에서 수동으로 초기화할 수 있습니다.
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를 수동으로 호출할 수 있습니다.

ts
import { expect, it, vi } from 'vitest';

// 테스트 실행 전 "VITE_ENV"는 "test"입니다.
import.meta.env.VITE_ENV === 'test';

it('changes value', () => {
  vi.stubEnv('VITE_ENV', 'staging');
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

it('다른 테스트를 실행하기 전에 값이 복원됩니다.', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
이전스냅샷
다음타입 테스트

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2024 Mithril Contributors

https://v0.vitest.dev/guide/mocking

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2024 Mithril Contributors