タイトルの通り今更ながらですが、フロントエンドのテストに入門しました。

弊社では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

吐き出されるのは、こんなやつとか

スクリーンショット 2019-09-22 14.10.13

こんなやつとか、、、、、、、、(焼け野原かな。。。。?😇😇😇)

スクリーンショット 2019-09-22 14.11.29

と、まあJest + Codecovでテストとカバレッジの確認ができるようになりました!
この焼け野原を見たら、テストする気にもなりますよね!!!!!!!!!!!!!!
カバレッジをcircleciのartifactsにアップロードしても悲しいかな誰も見ないので、PRなどで目につくようになるのもメリットだなと思いました!
皆さんも是非導入してみてください😎

今後は少しずつ書いていきながらテストの知見をためて、またシェアできればと思います。