Jest初心者が、VueにJestを導入〜単体テストを実施して、ハマった箇所と感想
この記事はTechCommit Advent Calendar 2022 10日目の記事です。
現在携わっているプロジェクトで、Vue.jsにJestを導入して、コンポーネントの単体テストを書いたので、Jest導入〜単体テストまでの経験を通してハマった箇所と感想をまとめてみたいと思います。
バックエンドのテストは書くけど、フロントエンドのテストは経験がないな、という方や、Jestは気になっているけど、どんな感じで書くの?どんなテストができるの? と思っているような方の参考になれば幸いです。
フロントエンドテスト全体として大事にすべき観点などは、TechCommit Advent Calendar 2022 の2日目に井上さんが書かれているJavaScript+Vue.jsの品質向上のためにしている基本的で大事な5つのコトの記事も参考にして頂ければと思います。
Jestを導入することになった背景
- プロジェクトはバックエンドがRails、フロントエンドがVue.jsで構成
- バックエンドはRspecのテストがあるが、フロントエンドはテストがなかった
- フロントエンドのJavaScript関連のバグが発生することが多く、品質担保が目的
筆者のバックグラウンド
- 主によく書くのはRails
- Railsの開発において、Rspecテストは普段から書いている
- Vue.jsも経験はあるが、歴が浅い
- フロントエンドのテストは書いたことがない
そもそも、Jestとは?
- Jestは、Facebookが開発しているJavaScriptのテストフレームワーク
- Babel, TypeScript, Node, React, Angular, vueなど、フロントエンド開発で広く利用されている
Jestの導入で行ったこと
環境・バージョン
- vue: 2.6.10
- Node: 16.18.0
Jestを利用するために、使用したライブラリ
Vue Test Utils
Vue.jsの公式が提供している、Vueコンポーネントをテストするための専用ユーティリティライブラリのVue Test Utilsを使用しました。
Vue Test Utilsは、Vueコンポーネントのコンパイル、コンポーネントのスタブ化やモックの注入など、テストに必要な機能を提供してくれます。
Vue Test Utilsのインストールは公式に従ってインストールしました。
Jest導入でハマったこと
【1】ハマったこと: vue-jestのバージョンの依存関係でハマった
Vue Test Utils
を導入する際に、Jest に *.vue
ファイルの処理方法を教えるために、vue-jest
プリプロセッサをインストールする必要があります。
ただ、npm install --save-dev vue-jest
でインストールしただけでは、テスト実行時に、下記のようにJavaScriptのsyntaxについて、Jestではサポートしていないと怒られてしました。
エラー内容
FAIL app/assets/javascripts/vue/components/test.spec.js
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
解決策
今回、vueのバージョン2の環境にJestを導入していた関係で、@vue/vue2-jest
を指定してインストールする必要がありました。
vueとjestのそれぞれのバージョンに応じたvue-jest
のバージョンを指定する必要があるので注意です。
vue-jestのバージョン
jest-environment-jsdom
が必要
【2】ハマったこと:vue-test-utilsの利用に、エラー内容
[vue-test-utils]: window is undefined, vue-test-utils needs to be run in a browser environment.
You can run the tests in node using jsdom
See https://vue-test-utils.vuejs.org/guides/#browser-environment for more details.
jestのv28からjsdomが標準でインストールされなくなったため、別途jest-environment-jsdom
をインストールする必要がありました。
Jestアップグレードガイド
解決策
- jest-environment-jsdomを別途インストール
npm install --save-dev jest-environment-jsdom
Vueコンポーネントの単体テスト(お気に入りボタン)
今回はjest
を使って、お気に入りボタンの単体テストを書きました。
具体的には下記の2点のテストコードを作成しました。
1. コンポーネントのロジックをテスト
2. スナップショットテスト
テスト対象のVueコンポーネント(お気に入りボタン)のロジック
- お気に入りボタンのクリック時に、axiosを使ってバックエンドのAPIに対して、postとdeleteのリクエストを送り、データを更新
- propsの値によってお気に入りボタンの表示を切り替える
【1】コンポーネントのロジックをテスト
Vueコンポーネント内で行っているAP通信に関しては、axios-mock-adapter
を使ってAPI通信をmock化させてテストしました。
また、非同期動作のためにflushPromises
を利用しました。flushPromisesを使うと、非同期処理を全て流すことができます。
it('methods:createLike お気に入りのボタンをクリックすると、likedがtrueになること', async () => {
const wrapper = mount(FavoriteButtton, {
propsData: {
id: 1,
like: false
}
})
const starButton = wrapper.find('.star-btn')
const axiosMock = new MockAdapter(axios)
axiosMock.onPost('/api/likes', { id: 1 }).reply(200)
expect(wrapper.vm.$data.liked).toBe(false)
starButton.trigger('click')
await flushPromises()
expect(wrapper.vm.$data.liked).toBe(true)
})
【2】スナップショットテスト
スナップショットテストは、UIの変更前の出力結果と変更後の出力結果を見比べて、差分を検知するテストです。今回、propsに任意の値を渡した場合に、期待するDOMが生成されることをテストしました。
スナップショットでテストした内容
- 任意のpropsを渡した場合に、期待するお気に入りボタンのclass名が存在すること
- Jestのマッチャーとして提供される
toMatchSnapshot
を使用して、DOMの差異の有無を検知
describe('props', () => {
it('未いいねの場合、star-btnが表示されること', () => {
const wrapper = mount(FavoriteButtton, {
propsData: {
id: 1,
like: false
}
})
expect(wrapper.find('.star-btn')).toBeTruthy()
expect(wrapper).toMatchSnapshot()
})
it('いいね済みの場合、unstar-btnが表示されること', () => {
const wrapper = mount(FavoriteButtton, {
propsData: {
id: 1,
like: true
}
})
expect(wrapper.find('.unstar-btn')).toBeTruthy()
expect(wrapper).toMatchSnapshot()
})
})
生成されるスナップショットファイル
toMatchSnapshot
を使うと、初回テスト実行時にスナップショットファイルが生成されます。
2回目以降のテストでは、DOMの生成に変化があった場合に検知して、スナップショットテストが落ちるようになります。
exports[`props いいね済みの場合、unstar-btnが表示されること 1`] = `
<div class="badges__action">
<div class="unstar-btn"><i class="fas fa-star"></i></div>
</div>
`;
exports[`props 未いいねの場合、star-btnが表示されること 1`] = `
<div class="badges__action">
<div class="star-btn"><i class="fas fa-star"></i></div>
</div>
`;
【その他】package.jsonにnpm scriptsの使い方を記述
スナップショットの変更内容を確認して、更新するためのnpm scriptsをpackage.json
に追加しました。今後、スナップショットが変更された時に、変更内容を確認した上で、更新を行うためのscriptsです。
-
test:watch
→変更の差異を確認 -
test:update
→スナップショットファイルを更新
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:update": "jest --updateSnapshot"
}
Jestを初めて触ってみての感想
- Jest関連のライブラリのバージョン依存などで、テスト実行できる状態にするまでの環境設定に時間がかかった
- テストの書き方は、RailsのRspecテストのコードと似ており、とっつきやすかった
- Jestのドキュメントが充実していて、公式見ながらやればテストコードを書くこと自体に苦はなさそうと感じた
- 導入するまでの腰は重いが、もっと早めに導入できれば良かったなと思う
- 既存のフロントエンドのコードにもどんどんテストを追加して、品質を担保していきたい
Jest導入時に参考にしたサイト・記事
Discussion