LoginSignup
79
82

More than 3 years have passed since last update.

Vueでよく書くユニットテストのパターン

Last updated at Posted at 2019-10-10

Vueのコンポーネントのユニットテストでよく書くパターンを紹介します。
環境はvue-cliで生成したものをそのまま使っています。

APIの詳細な説明はVue Test Utilsを参照してください。

スナップショットテスト

出力されるHTMLが予期せず変更されないようにする場合に使うテストです。

GachaMv.spec.ts
it("itemsの値がhtmlに出力されているか?", () => {
  const items = [
    new Item("アイテム", 3, "アイテム説明")
  ];
  const wrapper = shallowMount(GachaMv, {
    propsData: { items }
  });
  expect(wrapper.html()).toMatchSnapshot();
});

上記のテストを追加しnpm run test:unit or yarn test:unitを実行するとテストファイルと同階層に__snapshots__/GachaMv.spec.ts.snapが出力されます。

GachaMv.spec.ts.snap
exports[`GachaMv.vue itemsの値がhtmlに出力されているか? 1`] = `
<div class="gacha-mv">
  <slick-stub options="[object Object]">
    <div class="gacha-mv-list">
      <p class="gacha-mv-list-name">アイテム</p>
      <p class="gacha-mv-list-rare">3</p>
      <p class="gacha-mv-list-description">アイテム説明</p>
    </div>
  </slick-stub>
</div>
`;

shallowMountではなくmountを使用した場合は、以下のように子コンポーネントも展開した状態で出力されます。

GachaMv.spec.ts.snap
exports[`GachaMv.vue itemsの値がhtmlに出力されているか? 1`] = `
<div class="gacha-mv">
  <div class="slick-initialized slick-slider">
    <div class="slick-list draggable">
      <div class="slick-track" style="opacity: 1; width: 0px; transform: translate(0px, 0px);">
        <div class="slick-slide slick-current slick-active" data-slick-index="0" aria-hidden="false" style="margin-left: 0px; width: 0px;">
          <div>
            <div class="gacha-mv-list" style="width: 100%; display: inline-block;">
              <p class="gacha-mv-list-name">アイテム名</p>
              <p class="gacha-mv-list-rare">3</p>
              <p class="gacha-mv-list-description">アイテム説明</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
`;

以下のようにshallowMountとstubsを組み合わせることで、一部のコンポーネントのみスタブすることも可能です。

GachaMv.spec.ts.snap
const wrapper = shallowMount(GachaMv, {
  stubs: {
    'slick': Slick,
  },
}

computedが期待した値を返しているか?

computedが正しい値を確認しているかのテストです。

GachaMv.spec.ts
it("pickupItemが期待通りか?", async() => {
  const items = [
    new Item("アイテム名3", 3, "アイテム説明3"),
    new Item("アイテム名4", 4, "アイテム説明4"),
    new Item("アイテム名5", 5, "アイテム説明5"),
  ];
  const wrapper = shallowMount(GachaMv, {
    propsData: { items }
  });
  expect((wrapper.vm as any).pickupItems).toEqual([new Item("アイテム名5", 5, "アイテム説明5")]);
});

クリックイベントが動作しているか?

要素がイベントを発火した時に、定義した関数が実行されているかのテストです。
スタブにsinonを使ってます。

GachaPlay.spec.ts
it(".gacha-playクリック時にonPlayGachaが実行されるか?", () => {
  const onPlayGachaStub = sinon.stub();
  const wrapper = shallowMount(GachaPlay, {
    propsData: { 
      number: 1,
      onPlayGacha: onPlayGachaStub
    }
  });
  wrapper.find(".gacha-play").trigger('click');
  expect(onPlayGachaStub.called).toBe(true);
  onPlayGachaStub.restore();
});

sinon or jest.fn

先ほどのクリックイベントのテストでsinonを使いましたが、jest.fnでも同じことができます。
個人的は使い慣れているsinonを使用しています。

unit testing - stubbing a function using jest - Stack Overflow

非同期のテスト

非同期テストでは、flush-promisesやVue.nextTickを使わないと期待した結果が得られません。

flush-promisesについて

flush-promises

保留中の解決済みの約束のハンドラをすべてフラッシュします。

やっていることはmicrotasks または macrotasksのプロミスを返しているだけです。(setImmediate関数がある場合はmicrotasksが選択されるので、これがDOMの更新を待たなかった原因かもしれません。)

microtasks、macrotasksについは以下の記事が分かりやすかったです。

Tasks, microtasks, queues and schedules - JakeArchibald.com

Vue.nextTickについて

Vue.nextTick

callbackを延期し、DOMの更新サイクル後に実行します。DOM更新を待ち受けるために、いくつかのデータを変更した直後に使用してください。

テスト記述例

VueのDOMの更新後の値をテストする場合はnextTick、非同期処理(Promiseをfulfilled)する場合はflushPromisesを使用します。

it('test', async() => {
  const wrapper = shallowMount(Foo);
  await flushPromises(); // マウント時などに非同期処理がある場合
  expect(wrapper.vm.text).toBe('マウント時のテキスト');
  // expect(wrapper.find('text').text()).toBe('マウント時のテキスト'); // DOMが更新される保証はないので、Errorになるかも

  wrapper.find('button').trigger('click'); // クリックイベントで非同期処理を実行

  await wrapper.vm.$nextTick(); // DOMの更新を保証
  expect(wrapper.find('text').text()).toBe('クリック後のテキスト');
})

さいごに

簡単なコンポーネントのテストであれば、これらの組み合わせで十分かと思います。
今回のユニットテストではUIの崩れなど検知できないので、以下のリンク先で紹介されているようなstorybookやvisual regressionテストなどの導入を検討してみてください。

Storybookとvue-i18nで多言語確認を容易にしよう - スタディスト開発ブログ - Medium

Storybookとreg-suitで気軽にはじめるVisual Regression Testing - wadackel.me

79
82
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
79
82