如何給 Vue 的單元測試填充模塊檔?
TL;DR
Jest 有個 jest.mock
可以給 Vue 的單元測試,填充各種模塊檔。方法有兩種:
- 在模塊檔的目錄建立
__mocks__
給 Jest 用。然後jest.mock("模塊檔位置")
- 在測試檔給
jest.mock
的函式帶參數jest.mock("模塊檔位置", "模塊檔結構模擬")
問題
今天寫測試,寫了像是這樣的東東:
<template>
<main>
<p v-show="show">{{ text }}</p>
</main>
</template>
<script>
import { SomeAJAXAction } from "@/api/foobar.js";
export default {
name: "FooBar2000",
props: {
text: {
type: String,
default: "An example",
}
},
data: () => ({
show: true,
}),
async created() {
const res = await SomeAJAXAction( /* Some AJAX params */ );
this.show = res.data.data;
},
}
</script>
想說寫測試嘛,好像是 Jest 嗎?應該是這樣寫吧:
// test/unit/FooBar2000.soec.js
import { shallowMount } from "@vue/test-utils";
import FooBar2000 from "@/components/FooBar2000.vue"; // 慣例上 @/ 等於 src/
describe("FooBar2000", () => {
it("should render given text", () => {
wrapper = shallowMount(Home);
expect(wrapper.vm.text).toBe("An example");
});
});
但出現一些麻煩的錯誤。
錯誤
關於錯誤的具體情況省略,簡單來說,就是 AJAX 函式 SomeAJAXAction
需要瀏覽器的 window
屬性。但測試檔案因為是跑 node.js 的,當然不可能有 window
屬性。
我有試過填充 window
屬性,但幾乎沒有用。那怎麼辦呢?
「何不直接填充 SomeAJAXAction
函式?」
但怎麼做呢?
解決
正好 Vue.js Developers 有人寫下了解法。參考文章的作法,我這麼寫:
// src/api/__mocks__/foobar.js
export default {
SomeAJAXAction: () => ({ data: { data: true } })
};
// test/unit/FooBar2000.soec.js
import { shallowMount } from "@vue/test-utils";
import FooBar2000 from "@/components/FooBar2000.vue";
jest.mock("@/api/foobar.js");
describe("FooBar2000", () => {
it("should render given text", () => {
wrapper = shallowMount(Home);
expect(wrapper.vm.text).toBe("An example");
});
});
然後跑了測試,果然過了!Jest 真的會吃 __mocks__
檔案下的東西!
其他辦法
那麼有其他辦法嗎?有。還真的有。Jest 的說明手冊寫在 Mocking Partials at Mock Functions。
按照說明,我刪掉了 src/api/__mocks__/foobar.js
的檔案,把程式搬到測試:
// test/unit/FooBar2000.soec.js
import { shallowMount } from "@vue/test-utils";
import FooBar2000 from "@/components/FooBar2000.vue";
jest.mock("@/api/foobar.js", {
SomeAJAXAction: () => ({ data: { data: true } })
});
describe("FooBar2000", () => {
it("should render given text", () => {
wrapper = shallowMount(Home);
expect(wrapper.vm.text).toBe("An example");
});
});
這樣也通喔!
你甚至能給 AJAX Library 這麼引:
<script>
import axios from "axios";
export default {
async created() {
const res = await axios({
url: "/some/api",
method: "GET",
params: {
foo: "bar",
},
});
},
}
</script>
// test/unit/FooBar2000.soec.js
import { shallowMount } from "@vue/test-utils";
import FooBar2000 from "@/components/FooBar2000.vue";
import axios from "axios";
jest.mock("axios");
describe("FooBar2000", () => {
it("should render given text", () => {
axios.mockResolvedValue({ data: true });
});
});
參考資料
- jest mock window
- jest mock module