Logo

Vitest 처음 시작하기

Vite가 Webpack을 대체하는 차세대 번들러(bundler)로 부상하면서, 더불어 자매 제품인 Vitest도 Jest를 위협하면서 자바스크립트 생태계에서 입지를 넓혀가고 있습니다. 이번 포스팅에서는 차세대 테스팅 프레임워크로 각광받고 있는 Vitest에 대해서 살펴보는 시간을 가져보겠습니다.

Vitest란?

Vitest는 Vite를 기반으로 작동하는 테스팅 프레임워크입니다. 예전에 나왔던 Jest처럼 테스트 실행뿐만 아니라 모킹(mocking)과 스냅샷(snapshot)을 지원하며, Jest와 호환되는 API를 제공하고 있어서 Jest를 써보신 분이라면 어렵지 않게 Vitest를 쓸 수 있습니다.

Jest 대비 Vitest의 강점은 바로 간소화된 설정과 그로 인한 뛰어난 개발자 경험 (Developer Experience, DX)입니다. Jest를 사용할 때는 프로젝트의 빌드(build) 설정과 테스트 설정을 따로 해줘야하는 복잡함과 불편함이 있었습니다. 예를 들어, 프로젝트의 빌드 과정에서 애플리케이션 코드가 Babel을 통해 트랜스파일(transpile)이 되다면, 테스트 코드를 상대로도 동일한 변환이 일어나도록 Jest에 별도로 설정을 해줘야 했습니다.

하지만 Vitest는 테스트 코드만을 위한 별도의 설정 없이, Vite를 통해 프로젝트의 기본 빌드 설정을 그대로 이용할 수 있습니다. Vitest만 단독으로 쓸 수도 있지만, Vitest 진가는 Vite 프로젝트에서 사용했을 때 느낄 수 있습니다.

Vitest 설치

Vitest는 npm 패키지 저장소에 vitest라는 이름으로 올라와 있습니다. Vitest는 애플리케이션 실행에 필요한 의존성이 아니므로 개발 의존성으로 설치해야 합니다.

Node.js를 사용하는 프로젝트에서는 터미널에서 npm 명령어를 사용하여 Vitest를 설치합니다.

$ npm add -D vitest

차세대 자바스크립트 런타임인 Bun을 사용하는 프로젝트에서는 bun 명령어를 사용해서 설치합니다.

$ bun add -D vitest

이제 npxvitest 명령어를 실행할 수 있는데요. 터미널에서 다음과 같이 버전이 확인되면 정상적으로 설치가 된 것입니다.

$ npx vitest --version
vitest/1.0.4 darwin-arm64 node-v18.17.0

npx나 npm 명령어에 대한 자세한 설명은 관련 포스팅를 참고 바랍니다.

npm 스크립트 추가

프로젝트의 package.json 파일을 열고 Vitest를 실행해주는 세개의 스크립트를 추가해줍니다. 첫 번째 test 스크립트는 Vitest를 감시(watch) 모드로 실행해주고, 두 번째 test:run 스크립트는 Vitest를 일회성으로 실행해줍니다.

{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run"
  }
}

이렇게 자주 사용하게 되는 테스팅 관련 스크립트를 package.json 파일에 등록해주면, 프로젝트에 참여하고 있는 모든 개발자들이 Vitest 명령어 사용법을 정확히 몰라도 쉽게 Vitest를 실행할 수 있습니다.

테스트 코드 실행

그럼 한 번 간단한 테스트 코드를 작성하고 Vitest로 실행해볼까요?

first.test.js라는 파일을 생성하고, 그 안에 다음과 같이 입력합니다.

first.test.js
import { expect, test } from "vitest";

test("1 is 1", () => {
  expect(1).toBe(1);
});

그 다음 터미널에 npm run testbun run test 실행해보면 초록색 글씨로 테스트가 통과했다고 나올 것입니다.

$ npm run test

> vitest-temp@1.0.0 test
> vitest


 DEV  v1.0.4 /Users/daleseo/Temp/vitest-temp

 ✓ first.test.js (1)1 is 1

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  11:17:24
   Duration  122ms (transform 9ms, setup 0ms, collect 5ms, tests 1ms, environment 0ms, prepare 34ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

Vitest는 Jest와 달리 Vite처럼 기본적으로 감시(watch) 모드로 실행이 됩니다. 따라서 테스트 코드가 변경될 때 마다 테스트가 재실행됩니다.

예를 들어, toBe() 함수에 앞에 not.을 붙여볼까요?

first.test.js
import { expect, test } from "vitest";

test("1 is 1", () => {
  expect(1).not.toBe(1);
});

그러면 즉시 테스트가 재실행되어 결과로 실패가 나오는 것을 볼 수 있습니다.

 RERUN  first.test.js x3

 ❯ first.test.js (1)
   × 1 is 1

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  first.test.js > 1 is 1
AssertionError: expected 1 not to be 1 // Object.is equality
 ❯ first.test.js:4:17
      2|
      3| test("1 is 1", () => {
      4|   expect(1).not.toBe(1);
       |                 ^
      5| });
      6|

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed (1)
   Start at  12:05:44
   Duration  10ms


 FAIL  Tests failed. Watching for file changes...
       press h to show help, press q to quit

테스트 실행을 중단하고 싶다면 Ctrl + C를 눌러서 빠져나올 수 있습니다.

테스트를 일회성으로 실행하고 싶다면 npm run test:run을 실행하면 됩니다. 이 테스트 실행 방법은 주로 CI 환경에서 테스트를 실행할 때 활용됩니다.

$ npm run test:run

> vitest-temp@1.0.0 test:run
> vitest run


 RUN  v1.0.4 /Users/daleseo/Temp/vitest-temp

 ✓ first.test.js (1)1 is 1

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  12:08:46
   Duration  154ms (transform 12ms, setup 0ms, collect 5ms, tests 1ms, environment 0ms, prepare 47ms)

설정 파일

Vitest의 설정은 명령어를 실행할 때 옵션으로 넘길 수도 있지만, 설정 파일을 이용하면 좀 더 편리하게 설정할 수 있습니다.

Vite 프로젝트에서는 기존 vite.config.ts 파일에 test 항목만 추가하면 됩니다.

vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from "vite";

export default defineConfig({
  test: {
    // ...
  },
});

Vitest만 단독으로 사용하는 경우에는 vitest.config.ts 파일에 설정을 하면 됩니다.

vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    // ...
  },
});

Vite가 지원하는 다양한 설정에 대해서는 추후 별도 포스팅을 통해서 정리해드리도록 하겠습니다. 본 포스팅에는 자주 사용되는 두 가지 설정만 살펴볼게요.

필터링 설정

Vite는 프로젝트 내에 모든 테스트 파일을 찾아서 테스트를 실행해주는데요. 별다른 설정을 하지 않으면 기본적으로 파일명에 .test..spec.이 포함되어 있는 모든 자바스크립트와 타입스크립트 파일 및 JSX 파일까지 테스트 파일로 인식합니다.

만약에 특정 테스트 파일만 실행하고 싶은 경우에는 npm test <파일명 이나 경로>를 입력하면 됩니다.

$ npm test first

> vitest-temp@1.0.0 test
> vitest first


 DEV  v1.0.4 /Users/daleseo/Temp/vitest-temp

 ✓ first.test.js (1)1 is 1

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  12:10:23
   Duration  143ms (transform 16ms, setup 0ms, collect 5ms, tests 1ms, environment 0ms, prepare 50ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

include 설정을 통해서 Vitest가 테스트 파일을 찾을 때 포함시킬 경로를 명시할 수 있습니다.

예를 들어, 어떤 프로젝트에서 테스트 파일을 __test__ 디렉토리에 모아 놓고, 타입스크립트만 사용하며, 파일명에 .spec.이 포함되어야 한다면 다음과 같이, Glob 패턴을 사용하여 설정해줄 수 있습니다.

export default defineConfig({
  test: {
    include: ["__test__/*.spec.ts?(x)"],
  },
});

전역 함수 설정

Jest를 쓰시다가 Vitest로 넘어와서 가장 불편해하시는 부분이 expect, it, describe, test와 같은 함수들을 vitest 패키지로 부터 불러와야 한다는 것인데요.

이럴 때는 globals 옵션을 trie로 설정해주면 됩니다.

export default defineConfig({
  test: {
    globals: true,
  },
});

이제 테스트 코드에서 import 문을 제거해보세요. 테스트가 여전히 통과하는 것을 보실 수 있으실 것입니다.

first.test.js
// import { expect, test } from "vitest";

test("1 is 1", () => {
  expect(1).toBe(1);
});

마치면서

이상으로 Vitest로 간단한 테스트를 실행해보고, Vitest를 어떻게 설정하는지 살펴보았습니다. 본 포스팅이 Vitest를 처음 접하시는 분들에게 도움이 되었으면 좋겠습니다.

Vitest에 연관된 포스팅은 Vitest 태그를 통해서 쉽게 만나보세요!