Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What is going on with this article?
@shibe23

async/awaitを使った処理をVue.js + Vuex + Jest でユニットテストする

More than 1 year has passed since last update.

前回:
Vue.js+Vuexでasync/awaitを使ったPresentational and Container Component Pattern(サンプルつき)

前回は、Presentational and Container Component Patternを使って、async/awaitを使った簡易な会員登録フローを実装しました。

今回は、もう少し単純なコンポーネントを例として、Jestを使い、async/awaitを使用した処理を、ユニットテストする方法をまとめています。

テストする内容は下記のとおりです。

  • コンポーネント内のMethodsのテスト
  • Store内のテスト
  • Snapshotテスト(前回テスト時のDOMと差分がないか)

サンプルについて

コード全体はこちらからご覧ください。
https://github.com/shibe23/sandbox-vue-testable

ローカルで確認をする場合は、下記コマンドを実行してください。

npm install
npm run demo

動作確認環境

vue-cli:3.5.2
node.js : 8.11.4
npm: 5.6.0

コンポーネント内のテスト&スナップショットテスト

@vue/test-utilsを使用します。

コンポーネント内のテストのポイントは、

  • sharrowMount(またはMount)を使って、対象のコンポーネントだけをマウントする
  • コンポーネント内でテストをする範囲は「DOMを操作して、特定のActionが実行されたか」までとする
  • ActionによってStoreの中身が正しく変化したかどうかは、Storeのテストで確認する

実務でも、コンポーネント内の関数はDOM操作と紐づいた処理が多いため、このような方法で確認をするのがいいかと思います。

スナップショットテストのポイントは、

  • マウントしたHTMLとスナップショットを比較する形で行う

となります。

このテストで何を保証するか

  • コンポーネント内のボタンをクリックすると、Actionが実行される
  • 前回のHTMLのスナップショットと比較し、対象のDOMツリーが変更されていない

ユニットテストの実装例

tests\unit\ProductList.spec.js
import { mount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import Home from "@/views/Home.vue";
import ProductListModule from "@/store/ProductList";

const localVue = createLocalVue();

localVue.use(Vuex);

describe("ProductList.vue", () => {
  let actions;
  let state;
  let store;
  let wrapper;

  beforeEach(() => {
    state = {
      products: [
        {
          id: 1000,
          name: "T-shirts",
          price: 980,
          stock: 20
        }
      ]
    };

    actions = {
      FETCH_PRODUCTS: jest.fn()
    };

    store = new Vuex.Store({
      modules: {
        ProductList: {
          namespaced: true,
          state,
          actions,
        }
      }
    });
    wrapper = mount(Home, { store, localVue });
  });

  it("Clickボタンを押すと、actions.FETCH_PRODUCTSが呼ばれる", () => {
    const button = wrapper.find(".button");
    console.log(wrapper.text());
    button.trigger("click");
    expect(actions.FETCH_PRODUCTS).toHaveBeenCalled();
  });

  it("match snapshot", () => {
    expect(wrapper.html()).toMatchSnapshot();
  });
});

beforeEach()内のstate, actions, stateは、テスト用のダミーデータです。
Vuexのmodulesを使用している場合は、namespaced: trueをつける必要があります。

関数単位で検証をするというよりは、DOM内のイベントを発生させて、対象の関数をチェックするイメージです。
今回は、テストするコンポーネントの子コンポーネントに、クリックするボタンがあるため、shallowMountではなく、mountを使用しています、

DOM操作を行うテストとしては、e2eテストもありますが、ユニットテストの場合、ブラウザを起動しない分、実行速度はこちらの方が早くなります。

一方で、JestのSnapshotはDOM要素をテキストデータとして保存し、差分を比較するため、実行時の挙動は実際のブラウザと完全に同一ではない、という懸念があります。
どちらを採用するかは、テストの方針によって使い分けるのがいいかと思います。

Storeのテスト

Store内のデータに対して正しい値を取得できているか確認を行います。

このテストで何を保証するか

  • Actionsによって正しくStoreが変更されるか

ユニットテストの実装例

tests\unit\ProductListState.spec.js
import { createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import { mutations, actions } from "@/store/ProductList";
import { cloneDeep } from "lodash";

const state = {
  products: [
    {
      id: "",
      name: "",
      price: "",
      stock: ""
    }
  ]
};

const initStore = () => {
  return cloneDeep({
    modules: {
      ProductList: {
        namespaced: true,
        state,
        mutations,
        actions
      }
    }
  });
};

describe("ProductList.vue", () => {
  let store;

  beforeEach(() => {
    const localVue = createLocalVue();
    localVue.use(Vuex);

    store = new Vuex.Store(initStore());
  });

  it("商品一覧を取得できる", async () => {
    await store.dispatch("ProductList/FETCH_PRODUCTS");
    expect(store.state.ProductList.products[0]).toEqual({
      id: "1000",
      name: "T-shirts",
      price: 980,
      stock: 20
    });
  });
});

localVueを作成し、実際に使用しているVuexを適用します。
Action実行後、Store内のデータを比較し、テストケースの値と同じものか検証しています。

まとめ

@vue/test-utilsを使うことで、Vue.jsとVuexを使った環境でも、分かりやすい形で自動テストを実装することができます。

ユニットテストを導入することで、コンポーネントを実装する際に、どうすればテスト可能な単位となるかを考えるようになるため、積極的に導入していきたいです。

参考:
https://qiita.com/suzu-4/items/cf4f52049c681f4889e5

3
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
shibe23

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
3
Help us understand the problem. What is going on with this article?