LoginSignup
1
3

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-04-21

前回:
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/public-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

1
3
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
1
3