Vue.js + Vitest でテストを実行できる環境を作る

# はじめに
普段の業務では、主に Vue.js や Rails を書いている [@_kt15_](https://twitter.com/_kt15_) です。最近、Vitest が爆速らしいという情報を耳にしました。早速試そうとしたのですが、せっかくなら筆者がよく使う Vue.js と組み合わせて使ってみようと思ったので、まずは環境構築からやっていきます。本記事では、Vue.js + Vitest でテストを実行できる環境を作るところまでをゴールとします。

# Vitestとは？
:::note warn
2021/12/26 時点では、Vitest は安定版がまだ出ていないため、本番での使用は推奨されていないようです。
:::

[Vitest](https://vitest.dev/) は Vite ベースのユニットテストフレームワークです。
特徴はやはり**爆速なところ**でしょうか。速度面については[こちらの記事](https://zenn.dev/jay_es/articles/2021-12-22-vitest-comparison)で触れられており、 Jest との比較もあって分かりやすかったです。

>Given Jest's massive adoption, Vitest provides a compatible API that allows you to use it as a drop-in replacement in most projects. 

また、公式ドキュメントにはこのような記述もあり、 Jest との互換性もあるようです。

https://vitest.dev/guide/why.html

# 構築する環境
- Vue.js v3.2.26
- Node.js v16.13.0
- npm v8.1.0
- Vite v2.7.6
- Vitest v0.0.113

:::note info
Vitest を使うには、Vite v2.7 以上、 Node v14 以上のバージョンを使う必要があるみたいです。
:::

https://vitest.dev/guide/#adding-vitest-to-your-project

# 環境構築していく！
## Vite経由でVue.jsのプロジェクトを作成する
まずは、[Vite の公式ドキュメント](https://ja.vitejs.dev/guide/)を参考に Vite + Vue.js のプロジェクトを作成していきます。特に理由があるわけではないですが、本記事では npm を使用します。
`npm init vite@latest`を実行すると、いくつか質問されるのでそれぞれ下記のように入力していきます。入力後、プロジェクトを作成してくれるのでしばらく待ちます。（筆者の環境では、vite v2.7.6がインストールされました）

```terminal
$ npm init vite@latest

✔ Project name: … sample-vitest
✔ Select a framework: › vue
✔ Select a variant: › vue-ts

Scaffolding project in ...

Done. Now run:

  cd sample-vitest
  npm install
  npm run dev
```

プロジェクトの作成が終わったら、動作確認をしておきましょう。
下記のコマンドを実行し、http://localhost:3000/ を開いてください。

```terminal
cd sample-vitest
npm install
npm run dev
```

下記の画面が表示されれば、問題なくプロジェクトを作成できています🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201112/55fca1c3-a0b6-0858-44bb-0ca959322314.png)

## Vitestのセットアップ
### Vitestをインストールする
まずは Vitest をインストールします。筆者の環境では、v0.0.113 の Vitest がインストールされました。

```terminal
npm install -D vitest
```

### Vitestの設定ファイルを作成する

```terminal
touch vitest.config.ts
```

### テストするために必要な設定を追加する
作成した`vitest.config.ts`に下記の設定を追加していきます。
defineConfig には、 UserConfig 型に定義されているプロパティを指定できます。Vitest 側でこの [UserConfig 型を拡張し、 optional な test プロパティを追加](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/index.ts#L17-L22)しています。が、うまく読み込めていなかったので、`/// <reference types="vitest" />`を追記して、明示的に読み込むようにしました。（ここで少しハマりました・・）

```ts:vitest.config.ts
/// <reference types="vitest" />

import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    Vue(),
  ],
  test: {
    global: true,
    environment: 'happy-dom',
  },
})

```

指定できるオプションの詳細は下記を参照ください。

https://vitest.dev/config/#options

### テストを実行するためのnpmスクリプトを追加する

```diff_json:package.json
+ "test": "vitest",
```

必要な設定は以上です。

## テストを流してみる
せっかく設定したので、実際にテストを流してみましょう。

### テストに必要なライブラリをインストールする

```terminal
npm install @vue/test-utils@next @vitejs/plugin-vue happy-dom --save-dev
```

### specファイルを追加する
下記のコマンドを実行して、spec用のファイルを追加します。

```terminal
mkdir src/test
touch src/test/HelloWorld.spec.ts
```

雑な内容ですが、下記のテストを追加します。

```ts:HelloWorld.spec.ts
import { mount } from '@vue/test-utils'
import HelloWorld from '../components/HelloWorld.vue'
import { test, expect } from 'vitest'

test('初期表示', () => {
  const wrapper = mount(HelloWorld, {
    props: {
      msg: 'hello',
    },
  })

  expect(wrapper.text()).toContain('hello')
})
```

### テストを実行してみる
これで準備が整ったので、早速テストを実行してみましょう。`npm run test`を実行することで、テストが実行されます。

```terminal
$ npm run test

 √ src/test/HelloWorld.spec.ts (1)

Test Files  1 passed (1)
     Tests  1 passed (1)
      Time  851ms (in thread 7ms, 12499.63%)


 PASS  Waiting for file changes...
press any key to exit...
```

このように表示されれば、テストが実行されています🎉
念の為、うまくテストされているのかを確かめるためにわざと失敗するように修正してみます。

```diff_typescript:src/test/HelloWorld.spec.ts
import { mount } from '@vue/test-utils'
import HelloWorld from '../components/HelloWorld.vue'
import { test, expect } from 'vitest'

test('初期表示', () => {
  const wrapper = mount(HelloWorld, {
    props: {
      msg: 'hello',
    },
  })

- expect(wrapper.text()).toContain('hello')
+ expect(wrapper.text()).toContain('hello2')
})
```

先ほど実行した、`npm run test`が実行中であれば、上記の変更を保存すると自動でテストが再実行されます。終了していた場合は、`npm run test`を再度実行してください。

```
AssertionError: expected 'hello Recommended IDE setup: VSCode + VolarSee README.md for more information. Vite Docs  | Vue 3 Docscount is: 0 Edit components/HelloWorld.vue to test hot module replacement.' to include 'hello2'
 ❯ Proxy.<anonymous> node_modules/vitest/dist/vi-51946984.js:506:17
 ❯ Proxy.methodWrapper node_modules/chai/lib/chai/utils/addMethod.js:57:25
 ❯ src/test/HelloWorld.spec.ts:12:25
     10|   })
     11|
     12|   expect(wrapper.text()).toContain('hello2')
       |                         ^
     13| })

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

Test Files  1 failed (1)
     Tests  1 failed (1)
      Time  7ms


 FAIL  Tests failed. Watching for file changes...
```

期待通りテストが失敗したので、ちゃんとテストしてくれているようですね！

# 終わりに
今回は、Vue.js + Vitest の組み合わせでテストを実行できる環境を作りました。Vitest の爆速な部分をまだ体験できていないので、ぼちぼち触ってみようと思います。気が向いたら、次は試してみた系の記事を書こうかなと思います。

# 参考
- https://vitest.dev/
- https://ja.vitejs.dev
- https://zenn.dev/jay_es/articles/2021-12-22-vitest-comparison