LoginSignup
7
13

More than 3 years have passed since last update.

Vue Test Utils と Jest でコンポーネントのユニットテストを書く際のTips

Last updated at Posted at 2020-12-24

はじめに

Vueでコンポーネントの単体テストを書くにあたっての、やり方のメモを書き残します。

誰かのお役に立てれば幸いです。

私が過去に書いたJestの記事も参照してくださいね。

JestでVueコンポーネントとVuexストアの単体テストを書いてみよう!
https://qiita.com/karamage/items/e8757282e49e81f4d4eb

Vue Test Utils ガイドをよく読もう

Vueの単体テストを書くにあたって、初心者だし、何もわからんって人は、
まずは「Vue Test Utils ガイド」を読むと良いです。

このガイドはとてもわかり易いです。
https://vue-test-utils.vuejs.org/ja/guides/

VuexのMock化のやりかた

Vuexを使っているコンポーネントの単体テストを書く際は、Vuexをモック化する必要があります。
以下に、その際のテンプレートを貼っておきます。

test.js
import { shallowMount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import HogeCell from "@/components/HogeCell.vue";

const localVue = createLocalVue();

localVue.use(Vuex);

describe("HogeCellのテスト", () => {
  let store;
  let hogeStoreMock;
  let wrapper;
  const selectedHoge = {
    id: 12345,
    name: "からまげ",
  };
  beforeEach(() => {
    // Vuexストアのモックを作成する
    hogeStoreMock = {
      namespaced: true,
      actions: {
        setHoge: jest.fn(),
        setFuga: jest.fn()
      },
      getters: {
        selectedHoge: () => selectedHoge
      }
    };
    store = new Vuex.Store({
      modules: {
        hoge: hogeStoreMock
      }
    });
    // shallowMountだと子コンポーネントをスタブによって描画しなくなる(高速化)
    wrapper = shallowMount(HogeCell, {
      store,
      localVue,
      propsData: {
        hoge: selectedHoge
      }
    });
  });

  test("textに含まれていること", () => {
    expect(wrapper.find(".hoge_fuga").text()).toMatch("hogehoge");
  });

  // ... 略
});

Element UIを使うコンポーネントのテストのやりかた

import { shallowMount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import ElementUI from 'element-ui'; 

const localVue = createLocalVue();

localVue.use(Vuex);
localVue.use(ElementUI);

describe("test", () => {
...

カスタムディレクティブのモック化のやりかた

   wrapper = shallowMount(HogeCell, {
      // 略
      directives: {
        hogeHogeDirective() {}
      }
    });

アラートのテストの書き方

test.js
  test("名前の入力がない状態で送信ボタンを押した際、アラートが表示され、送信ができないことを確認する", async () => {
    await wrapper.setData({ name: "" });
    const mock = jest.fn();
    window.alert = jest.fn();
    wrapper.setMethods({ post: mock });
    await wrapper.vm.tapSend();
    expect(mock).not.toBeCalled();
    expect(window.alert).toBeCalled();
  });

LocalStorageのMock化のやりかた

test.js
class LocalStorageMock {
  constructor() {
    this.store = {};
  }

  clear() {
    this.store = {};
  }

  getItem(key) {
    return this.store[key] || null;
  }

  setItem(key, value) {
    this.store[key] = value.toString();
  }

  removeItem(key) {
    delete this.store[key];
  }
}

global.localStorage = new LocalStorageMock();
describe("HogeForm", () => {
  // 略

  beforeEach(() => {
    global.localStorage.setItem("HOGE_KEY", "hogehoge");
    // 略
  })
})

オフラインの場合のテストのNavigatorのモック化のやりかた

test('should log "offline"', () => {
    const logSpy = jest.spyOn(console, 'log');
    jest.spyOn(navigator, 'onLine', 'get').mockReturnValueOnce(false);
    main();
    expect(logSpy).toBeCalledWith('offline');
  });

非同期処理やnextTickのテストのやりかた

it('test', async() => {
  const wrapper = shallowMount(Foo);
  await flushPromises(); // mount時に非同期処理がある場合、待つ
  expect(wrapper.vm.text).toBe('マウント時のテキスト');

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

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

window.location.href のモック化


global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, 'location', {
  value: {
    href: url
  }
});
expect(window.location.href).toEqual(url);  

モック関数の呼び出し回数を取得したい場合

  test("テスト", async () => {
    await flushPromises();
    expect(
      hogeStoreMock.actions.getHoge.mock.calls.length
    ).toBe(1);
    await wrapper.setData({ isLast: false });
    wrapper.vm.next();
    await flushPromises();
    expect(
      hogeStoreMock.actions.getHoge.mock.calls.length
    ).toBe(2);
  });

ユーザーの入力値のテストの書き方

it("フォームを更新するとお知らせを表示", () => {
  const wrapper = shallowMount(FormSubmitter, {
    mocks: {
      $http: mockHttp
    }
  })

  wrapper.find("[data-username]").setValue("karamage")
  wrapper.find("form").trigger("submit.prevent")

  expect(wrapper.find(".message").text())
    .toBe("karamageさん、お問い合わせ、ありがとうございます。")
})

watch内の関数を呼び出したい場合

wrapper.vm.$options.watch.bar.call(wrapper.vm)

Circle CI 上での jest テストが not enough memory となる場合の対処

テストいっぱい書くと、プロセスが立ち上がりすぎてメモリ不足で失敗することがある。
https://qiita.com/takewell/items/7b7731ffbfcbc5403efd

カバレッジの詳細を見る方法

./coverage/lcov-report/index.html を開くと詳細が見れる。

スクリーンショット 2020-12-24 17.08.40.png

7
13
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
7
13