最近Nuxt.jsで作成しているアプリケーションに後からjestを導入したので、その辺の手順をメモしておきます📝
- Jestとは?
- Nuxt.jsにJestを導入する
- 発生したエラーとTips
- [Vue warn]: Unknown custom element: <v-icon> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
- [Vue warn]: Unknown custom element: <nuxt-link> - did you register the component correctly? For recursive components, make sure to provide the “name” option.
- [Vue warn]: Error in render: “TypeError: Cannot read property ‘resolve’ of undefined”
Jestとは?
JestはJavaScriptのテスティングフレームワークです。
基本的な構文は下記のような感じです、若干Rspecに近い感じがしますね👀
const sum = function sum(a, b) { return a + b; } describe('sample spec', () => { it('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); })
詳しい説明等は公式のドキュメントをご確認ください🙋
Nuxt.jsにJestを導入する
前提
今回のNuxt.jsとTypeScriptとJestのバージョン情報は下記のとおりです。
- nuxt: 2.10.2
- typescript: 3.7.4
- jest: 25.1.0
依存ライブラリのインストール
まず使用するためにJest本体と関連ライブラリをinstallします📦
// Jest本体とVue,TypeScriptのサポート用のライブラリをインストール npm install --save-dev jest ts-jest vue-jest @vue/test-utils @types/jest // Jest内で最新のJS構文を使用するためにBabel関連のライブラリもインストール npm install --save-dev npm install --save-dev babel-jest babel-core babel-preset-env
Jestの設定
Jestを使うにあたっての設定は特に必要なありません 🙆♂️
zero config Jest aims to work out of the box, config free, on most JavaScript projects. https://jestjs.io/ja/
※必要に応じてjest --init
でデフォルトの設定ファイルを作成してカスタマイズ可能
なのでJestでBabelを使うための設定飲みを行います⚙
Babelの設定は下記のような.babelrc
をプロジェクトのルートディレクトリに配置すればOKです👍
{ // babel-preset-envを使用することを明示、変換先をfalse(何もしない)に指定 "presets": [["env", { "modules": false }]], "env": { "test": { // babel-preset-envを使用することを明示、対象環境をnodeに指定 "presets": [["env", { "targets": { "node": "current" } }]] } } }
参考:
Jestの実行コマンドを追加
npm run test
でJestが実行できるようにpackage.json
にscript
を追加します🏃
"scripts": { "test": "jest --config jest.config.js spec/**/*.js"
テスト用のファイル.spec.js
を追加
今回は下記のようなVueコンポーネントをテストするSpecをサンプルで追加しました。
import Vue from 'vue' import Vuetify from 'vuetify' import { mount } from '@vue/test-utils' import Favorite from '@/components/foods/Favorite.vue' Vue.use(Vuetify) describe('components/Favorite.vue', () => { it('is a Vue instance', () => { const wrapper = mount(Favorite) expect(wrapper.isVueInstance()).toBeTruthy() }) describe('toggeleFavorite', () => { describe('is not favorited.', () => { it('toggle favorited.', () => { const wrapper = mount(Favorite, { propsData: { favorited: false } }) wrapper.vm.toggeleFavorite() expect(wrapper.vm.currentFavorited).toBe(true) }) }) describe('is favorited.', () => { it('toggle unfavorited.', () => { const wrapper = mount(Favorite, { propsData: { favorited: true } }) wrapper.vm.toggeleFavorite() expect(wrapper.vm.currentFavorited).toBe(false) }) }) }) })
テストを実行
無事にテストが実行できました🙌
npm run test > nuxt-app@1.0.0 test /nuxt-app > jest --config jest.config.js spec/**/*.js PASS spec/components/Favorite.spec.js (9.758s) components/Favorite.vue ✓ is a Vue instance (40ms) toggeleFavorite is not favorited. ✓ toggle favorited. (8ms) is favorited. ✓ toggle unfavorited. (5ms) favoritedColor is not favorited. ✓ return unfavorited color. (4ms) is favorited. ✓ return favorited color. (15ms) Test Suites: 1 passed, 1 total Tests: 5 passed, 5 total Snapshots: 0 total Time: 15.486s
CIでテストを実行する
さらに今回はCircleCIを使ってCI上でJestとESLintを実行するようにしてみました、設定ファイルは下記のような感じです⚙
version: 2 jobs: build: docker: - image: circleci/node:12 steps: - checkout - restore_cache: key: dependency-cache-{{ checksum "package-lock.json" }} - run: npm install - save_cache: key: dependency-cache-{{ checksum "package-lock.json" }} paths: - ./node_modules - persist_to_workspace: root: . paths: - . lint: docker: - image: circleci/node:12 steps: - attach_workspace: at: . - run: npm run lint test: docker: - image: circleci/node:12 steps: - attach_workspace: at: . - run: npm run test workflows: version: 2 build-test-lint: jobs: - build - lint: requires: - build - test: requires: - build
Workflowを使ってJestによるテストとESLintによる静的解析が並列で走るようになっています🤖
発生したエラーとTips
Vueのコンポーネントをテストしてるときにハマったことを色々メモしておきます📝
[Vue warn]: Unknown custom element: <v-icon> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Vuetifyで定義されたコンポーネントが認識できていないことが原因、Vue.use(Vuetify)
を実行してVuetifyを使うことを明示してあげたところ解決した。
import Vue from 'vue' import Vuetify from 'vuetify' Vue.use(Vuetify)
[Vue warn]: Unknown custom element: <nuxt-link> - did you register the component correctly? For recursive components, make sure to provide the “name” option.
Nuxtで定義されたコンポーネントnuxt-link
を認識できていないことが原因、RouterLinkStub
を使ってstubして解決した。
import { mount, RouterLinkStub } from '@vue/test-utils' const wrapper = mount(Component, { stubs: { NuxtLink: RouterLinkStub } })
[Vue warn]: Error in render: “TypeError: Cannot read property ‘resolve’ of undefined”
前回と違って今回はto
オプションを使ってリンク先をしていたため、RouterLinkStub
を使ったStubがうまく使えなかったので別の方法を使う必要がありました。
<v-btn value="recent" to="/foods"> <span>Recent</span> <v-icon>mdi-history</v-icon> </v-btn>
実際に行った方法は、下記のような形でLocalVueを使ってRouterオブジェクトを作成して渡してあげることで解決できました。
import Vue from 'vue' import VueRouter from 'vue-router' import { mount, createLocalVue } from '@vue/test-utils' const localVue = createLocalVue() localVue.use(VueRouter) const router = new VueRouter() const wrapper = mount(Component, { localVue, router })