UIコンポーネントのテスト

Vue.jsでUIコンポーネントをテストする方法について確認します。「UIコンポーネントのテストで確認すべきこと」「vue-test-utils」について取り上げています。

環境

Vue CLI で動作確認用のプロジェクトを構築しています。

$ vue create vue-component-test


Vue CLI v3.4.1
┌───────────────────────────┐
│  Update available: 3.5.0  │
└───────────────────────────┘
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Router, Vuex, CSS Pre-processors, Linter, Unit, E2E
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Stylus
? Pick a linter / formatter config: TSLint
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Pick a unit testing solution: Jest
? Pick a E2E testing solution: Cypress
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

Unit Testing を選択したため、vue-test-utils もインストール済みです。
ユニットテストツールには Jest を選択しています。

vue-test-utilsでShallow描画

Vue CLI で構築したプロジェクトには、すでに以下テスト( tests/unit/example.spec.ts )が作成されていました。

import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message';
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg },
    });
    expect(wrapper.text()).toMatch(msg);
  });
});

HelloWorldコンポーネントShallow描画 してテストしているようです。

Shallow描画とは?
「テスト対象のコンポーネント」が、子コンポーネントを持っている場合、子コンポーネントの部分はスタブ化されて描画せずに済みます。

テストの実行

yarn test:unit でユニットテストを実行できます。

--watchオプション もあるようです。

$ yarn test:unit --help
yarn run v1.13.0
$ vue-cli-service test:unit --help

  Usage: vue-cli-service test:unit [options] <regexForTestFiles>

  Options:

    --watch   run tests in watch mode

  All jest command line options are supported.
  See https://facebook.github.io/jest/docs/en/cli.html for more details.

✨  Done in 0.46s.

テストで確認すること

vue-test-utils のドキュメントでは、ホワイトボックスなテスト よりも ブラックボックスなテスト が推奨されているようでした。コンポーネント内にどんなメソッドがあるのかは関知せずに、主に以下のことを検証すれば良いかと思います。

  • propsに値を渡して、期待した動きになっていること
  • ユーザー操作によって、イベントが発火されて、期待した動きになっていること

テストコンポーネント

例として、下記コンポーネントをテストしてみます。

<template>
  <div
    @click="click"
    :class="{ outline: outline }">
    <slot></slot>
  </div>
</template>

<script>
export default {
  props: {
    outline: {
      type: Boolean,
      default: false,
    },
  },

  methods: {
    click() {
      this.$emit('click');
    },
  },
};
</script>

テストコード

import {shallowMount} from '@vue/test-utils';
import Btn from '@/components/Btn.vue';

describe('Btn.vue', () => {
    describe('props', () => {
        it('does not have the outline class', () => {
            const wrapper = shallowMount(Btn);
            expect(wrapper.classes()).not.toContain('outline')
        });

        it('has the outline class', () => {
            const wrapper = shallowMount(Btn, {
                propsData: {outline: true},
            });
            expect(wrapper.classes()).toContain('outline')
        });
    });

    describe('event', () => {
        it('click event is executed', () => {
            const wrapper = shallowMount(Btn);
            wrapper.trigger('click');
            expect(wrapper.emitted().click).toBeTruthy();
        });
    })
});

propsに値を渡しての確認ユーザー操作の確認describe を分けています。

テスト実行

テストを実行してみます。

$ yarn test:unit tests/unit/btn.spec.ts 
yarn run v1.13.0
$ vue-cli-service test:unit tests/unit/btn.spec.ts
 PASS  tests/unit/btn.spec.ts
  Btn.vue
    props
      ✓ does not have the outline class (17ms)
      ✓ has the outline class (2ms)
    event
      ✓ click event is executed (4ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        3.72s
Ran all test suites matching /tests\/unit\/btn.spec.ts/i.
✨  Done in 5.35s.

参考