はじめに
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 を開くと詳細が見れる。