4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vue3+TypeScript+Jest】Vue Test Utilsを用いた画面遷移のテスト

Last updated at Posted at 2021-09-17

![Vue CLI-4.5.13](https://img.shields.io/badge/Vue CLI-4.5.13-) Vue-3.0.0 TypeScript-4.1.5 jest-24.0.19

はしがき

Jestで画面遷移のテストをしたくなったのですが、ドンピシャな記事がなかなか出てこなくてしんどかったので、ちょっと調べてサンプルを組んでみました。
フロントエンドのテストコードに取り組んで1日目なので、変なところがあるかもしれませんがご容赦ください。

目次

  1. 前提
  2. コード
  3. コード解説
  4. まとめ

前提

Vue CLIで生成したプロジェクトを使っています。
Vue3TypeScriptJestの3つが満たせていればよいので、それ以外の値は適当です。

Vue CLIの実行

$ vue create test-project

Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, TS, Router, Vuex, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? Yes
? Save preset as:

プロジェクト構成(特に手は加えないです)

(root)
├── router
│   └── index.ts
├── views
│   ├── About.vue
│   └── Home.vue
├── App.vue
└── tests 
    └── unit
        └── example.spec.ts

コード

完成系は以下の通りです。変更したファイルだけ載せています。

views/Home.vue
<template>
  <router-link to="/about">Aboutヘ</router-link>
</template>
App.vue
<template>
  <router-view />
</template>

<style>
# app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>
example.spec.ts
import{ mount, flushPromises } from "@vue/test-utils";
import router from "@/router";
import Home from "@/views/Home.vue";
import About from "@/views/About.vue";
import { defineComponent } from "@vue/runtime-dom";

// 確認用の各画面のWrapper
const homeWrapper = mount(Home, {
  global: {
    plugins: [router],
  },
});
const aboutWrapper = mount(About);

describe("HelloWorld.vue", () => {
  it("Home.vueからAbout.vueへの遷移", async () => {
    // wrapperにHome.vueを表示させる(初期位置の設定)
    router.push("/");
    await router.isReady();

    // 実際にテストで動かす画面を作成
    const testApp = defineComponent({
      template: `<router-view />`,
    })
    const wrapper = mount(testApp, {
      global: {
        plugins: [router],
      },
    });

    // 遷移前の画面のHTMLがHome.vueと同一か確認
    expect(wrapper.html()).toBe(homeWrapper.html());

    // <a>タグ(今回は<router-link>しかない)を取得し、押下処理
    await wrapper.find("[data-test='router']").trigger("click");
    await flushPromises();

    // 遷移後の画面のHTMLがAbout.vueと同一か確認
    expect(wrapper.html()).toBe(aboutWrapper.html());
  });
});

コード解説

公式ドキュメントのTesting Vue Routerに少し手を加えた程度のものになります。
example.spec.tsがメインなので、順に説明します。

Home.vueAbout.vueのWrapperを作成

実際に画面を動かすWrapperはテストの中で定義しますが、それとは別に静的に画面の情報(=HTML)を取得したいので、Home.vueAbout.vueのWrapperを事前に作成しておきます。

// 確認用の各画面のWrapper
const homeWrapper = mount(Home, {
  global: {
    plugins: [router],
  }
});
const aboutWrapper = mount(About);

VueWrapperの作成については公式ドキュメントを参照してください。
Home.vueでは<router-link>を使っているので、<router-link><a>タグに展開させるため、routerglobal.pluginマウントオプションで読み込む必要があります。

具体的に言うと以下の部分ですね。

    plugins: [router], // ここでrouter/index.jsで定義されているRouterを読み込みます

RouterLinkStubを使う手もあるのですが、RouterLinkStubを使用した場合は<router-link>のまま出力されるため、今回はやりたいことに沿っておらず使わないことにしました。
(リンク先のテストなどではtoを使うことでコードを短くできるはずです)

routerを活性化

今回はrouterで画面遷移を行うので、routerが使えるようにしないといけません。
ただそのままだと使えないようなので、routerの初期位置を指定したうえで、router.isReady()を使ってrouterが使える状態になったか確認する必要があります。

参考:https://next.vue-test-utils.vuejs.org/guide/advanced/vue-router.html#using-a-real-router

Although it's not entirely clear from the warning, it's related to the fact that Vue Router 4 handles routing asynchronously.

今回はHome.vueからAbout.vueへの遷移なので、Home.vue(=/)を初期位置に設定します。

    // wrapperにHome.vueを表示させる(初期位置の設定)
    router.push("/");
    await router.isReady();

実際に動作させるWrapperを作成

routerの準備ができたところで、動かすためのWrapperを作成します。
今回はテスト用に<router-view />だけ記載されているAppを作成して使うことにしました。

    const testApp = defineComponent({
      template: `<router-view />`,
    })
    const wrapper = mount(testApp, {
      global: {
        plugins: [router],
      },
    });

画面遷移すると、この<router-view />が更新される感じですね。

画面遷移のテストを実施

これでテストの準備ができたので、実際にテストをしていきます。
<router-link>を押下した際に、Home.vueからAbout.vueに画面が切り替わっている状態を想定しているため、それの確認を行います。

    // 遷移前の画面のHTMLがHome.vueと同一か確認
    expect(wrapper.html()).toBe(homeWrapper.html());

    // <a>タグ(今回は<router-link>しかない)を取得し、押下処理
    await wrapper.find("[data-test='router']").trigger("click");
    await flushPromises();

    // 遷移後の画面のHTMLがAbout.vueと同一か確認
    expect(wrapper.html()).toBe(aboutWrapper.html());
  });

wrapper.html()await wrapper.find("[data-test='router']").trigger("click");の前後で変わるため、それを比較する感じですね。

flushPromisesをしないとrouterの遷移が完了する前に次に行ってしまいます。

参考:https://next.vue-test-utils.vuejs.org/guide/advanced/vue-router.html#using-a-real-

Again, due to Vue Router 4's new asynchronous nature, we need to await the routing to complete before making any assertions.

In this case, however, there is no hasNavigated hook we can await on. One alternative is to use the flushPromises function exported from Vue Test Utils:

まとめ

とりあえずJestで画面遷移が確認できることが確認できたので良かったです。
わかってしまえば単純なのですが、前提知識がないためかrouter周りで苦労しました。

遷移先のパスが同一か、という記事は結構出てきたのですが、実ページまで確認するようなものはあまり見当たらなかったのですが、Jestで非推奨なのでしょうか?

サンプルはGithubに置いています。
https://github.com/kanade-nishikawa/vue-transition-test-sample

最後までありがとうございました。

4
0
0

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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?