Nuxt.js + Jest + Codecovでフロントエンドのテストに入門した
2019.09.22
みなさんフロントエンドのテスト、書いてますか? 弊社でもフロントエンドのテストもちゃんとやらないとなってことで、Nuxt.jsを利用したプロジェクトでJest + Codecovで、Unit Testをし、Coverageを確認できるようにしました。
タイトルの通り今更ながらですが、フロントエンドのテストに入門しました。
弊社ではNuxt.jsを利用したプロジェクトが多いので、まずはそこに JSのテストフレームワークである Jest と カバレッジ確認用に Codecov を追加しました。
以下のコマンドで必要なpackageをインストールします。
for Jest
$ yarn add -D jest vue-jest ts-jest babel-jest @vue/test-utils babel-preset-vue-app
for Codecov
$ yarn add -D codecov
その後、package.jsonにscriptを追加しておきます。
test:ciではcoverageを吐き出したいので、optionを追加しています。
"scripts": {
"test": "jest --config jest.config.js",
"test:ci": "jest --config jest.config.js --coverage"
},
今回はアプリケーションのルートディレクトリに tests
ディレクトリを用意して、その中にファイルを入れています。
mock用のデータもそこに入れています。
ファイル名は変えておりますが、階層構造はこんな感じ。
rootDir
└──
tests
└── unit
├── _mockData
│ └── posts.json
├── compounds
│ └── button
│ └── ButtonDefault.spec.js
└── organisms
└── list
└── ListButton.spec.js
あとは、jest用の設定ファイル jest.config.js
を用意してやります。
Nuxtで(部分的に)tsを利用しているため、 preset: 'ts-jest',
も追加しております。
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
'^.+\\.ts$': '<rootDir>/node_modules/ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/app/$1',
'^~/(.*)$': '<rootDir>/app/$1',
},
moduleFileExtensions: ['js', 'json', 'vue', 'ts'],
testMatch: [
'<rootDir>/tests/unit/**/*.js',
'<rootDir>/tests/unit/**/*.ts',
],
globals: {
'ts-jest': {
tsConfig: 'tsconfig.json',
},
},
collectCoverage: false,
collectCoverageFrom: ['<rootDir>/app/components/**/*.vue', '<rootDir>/app/pages/**/*.vue'],
coverageDirectory: './coverage/',
coverageReporters: ['json', 'text-lcov', 'lcov'],
}
諸々のファイル準備が終わったら、テストを書いていきましょう。
今回テストしたいファイルは、以下の components/organisms/list/ListButton.vue
です。
<template>
<ul class="list">
<li
v-for="(post, i) in posts"
:key="i"
>
<a
@click="$emit('clickItem', post)"
>
{{ post.name }}
</a>
</li>
<li>
<a
@click="$emit('clickAdd')"
>
新規追加する
<svg viewBox="0 0 24 24">
<use xlink:href="#plus" />
</svg>
</a>
</li>
</ul>
</template>
<script>
import VueTypes from 'vue-types'
export default {
props: {
post: VueTypes.arrayOf(VueTypes.shape({
name: VueTypes.string.isRequired,
description: VueTypes.string,
})).isRequired.loose,
},
}
</script>
このファイルのテスト観点は
- propsで渡されているデータ
- click eventが正しく動作しているかどうか
としました。
import { mount, shallowMount } from '@vue/test-utils'
import posts from '../../_mockData/posts.json'
import ListButton from '@/components/organisms/list/ListButton.vue'
describe('components/organisms/list/ListButton.vue', () => {
const propsData = {
list: posts.list,
}
test('props', () => {
const wrapper = mount(ListButton, { propsData })
expect(wrapper.props()).toEqual(propsData)
})
test('check click link event', () => {
const wrapper = shallowMount(ListButton, { propsData })
// 全てのa tagを取得
const links = wrapper.findAll('a')
links.wrappers.forEach((link, index, links) => {
// 最後のa tagだけ、機能が異なるので、テストを分岐
if (index === 0) {
link.trigger('click')
// click eventが取れているか確認する
expect(wrapper.emitted().clickItem).toBeTruthy()
// click eventで渡される内容が指定のものか確認する
expect(wrapper.emitted().clickItem[0][0]).toBe(posts.list[0])
} else if (index === (links.length - 1)) {
link.trigger('click')
expect(wrapper.emitted().clickAdd).toBeTruthy()
}
})
})
})
あとは、
$ yarn test
とローカルで実行してやると、内容をチェックしてくれます。
ここまでやると、coverageとかもチェックしたくなりますよね。
弊社ではCircleCIを使っているのでConfigに以下を追加してやります。
(circle ciの設定画面でtokenの設定も必要ですが)これだけで、coverageをいい感じに図示してくれます!
- run:
name: Testing and export coverage report
command: yarn test:ci
- run:
name: Codecov
command: yarn codecov
吐き出されるのは、こんなやつとか
こんなやつとか、、、、、、、、(焼け野原かな。。。。?😇😇😇)
と、まあJest + Codecovでテストとカバレッジの確認ができるようになりました!
この焼け野原を見たら、テストする気にもなりますよね!!!!!!!!!!!!!!
カバレッジをcircleciのartifactsにアップロードしても悲しいかな誰も見ないので、PRなどで目につくようになるのもメリットだなと思いました!
皆さんも是非導入してみてください😎
今後は少しずつ書いていきながらテストの知見をためて、またシェアできればと思います。