這期介紹一款比較別致的 mock 工具,mirage.js。說它別致,緣由是與競品有一點點區別:它是在客戶端啟動的一個 mock 服務,恰如“海市蜃樓”為遠程調度展現一幅 api 全景圖;實則是一種偽裝技術,在應用內存里攔截了所有請求。
Mock API
上次有人在評論區問我,前端開發時怎樣避免啟動一堆后端服務
。理想狀態下(理想狀態下,理想狀態下,重要的事情說三遍),有 mock 服務就行了——瀏覽器裝個代理插件,將 api 路由到 mock 服務上。Mock 服務器可以是后端倉庫里一個本地服務;也可以是線上的三方產品——比如國產開源的yapi——直接在線編輯。現代開發一般就是前后分離,前端開發時,后端小伙伴很可能還沒開工;mock api 就可以幫你擺脫這類窘境。當然,你習慣于前端 hard code,我無話可說……
OK,答疑結束,回到本期話題。Mirage JS 就是一款 API Mock 工具,比之常規 mock 服務,你不用裝代理插件了,因為它與前端代碼一起運行。接著看看怎么使用吧。
Guide
安裝就一筆帶過了——yarn add -D miragejs
然后在前端 src 目錄下寫一個 server.js 文件,用來定義所有的 mock api。代碼如下所示:
// server.js
import { Server } from "miragejs";
export function mockServer() {
return new Server({
routes() {
this.namespace = "api";
this.get('/todos', () => [ "Buy Onion" ]);
}
})
}
Mirage 代碼還是挺直白的:在routes
方法里定義了一個 Get API——/api/todos
——返回我們所需要的 mock 數組[ "Buy Onion" ]
。一個最簡單的 mock 服務就完工了。我們以 vue 項目為例,在入口文件 main.js 里引入 server.js,并新建這個 mock 服務。
import Vue from "vue";
import { mockServer } from "./server";
mockServer();
new Vue({
render: h => h(App)
}).$mount("#app");
和平日里一樣,用 webpack 啟動 vue 項目,該 mock 服務就跟隨整個前端項目啟動了,并且共享 webpack 熱加載。與傳統的本地 mock 服務比起來,你可以少打一行命令,減少了 50%的操作。至于前端組件,和往常一樣不需要做任何改變:
// Todos.vue
<template>
<ul>
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
</template>
<script>
export default {
async created() {
const {data} = await axios.get("/api/todos");
this.todos = data;
}
}
</script>
看一下效果吧,頁面根據 mock api 顯示列表,并在修改 mock 數據后,自動熱加載更新。
More
當然,Mirage 的功能不止如此,它順便集成了一套簡單的前端數據庫,實現了常規的增刪改查交互操作。
export function makeServer() {
return new Server({
seeds(server) {
server.db.loadData({
todos: [
{ text: "Buy Onion", isDone: false },
{ text: "Buy Garlic", isDone: false }
]
});
},
routes() {
this.namespace = "api";
this.get("/todos", ({ db }) => {
return db.todos;
});
this.patch("/todos/:id", (schema, request) => {
let todo = JSON.parse(request.requestBody).data;
return schema.db.todos.update(todo.id, todo);
});
this.post("/todos", (schema, request) => {
let todo = JSON.parse(request.requestBody).data;
return schema.db.todos.insert(todo);
});
this.delete("/todos/:id", (schema, request) => {
return schema.db.todos.remove(request.params.id);
});
}
});
}
大家可以看下面這個圖片,交互數據全部來自 Mirage 的 mock api:
實現一套前端 DB 來維護各色 mock api,在實際開發中還是有一定難度的。只有配合極佳的前后端團隊才能做到:
- 前后端商討一份接口方案
- 各自回頭開發,開發階段全程啟動 mock 模式
- 前后端代碼合并
- 生產環境調試——唉,完美無缺!
現實工作中,前端很可能還是會依賴真實的生產數據開發,Mock API 大概率就沒人鳥了。不過,測試代碼倒是一直需要 Mock 數據。
Mock 測試
測試環境——尤其在一些沙箱環境里——調用其他服務的 api 一直很不方便的。比如上面的 Todos.vue 組件,加載前需要請求 api;在傳統的單元測試環境里,提供這個異步請求就很麻煩了。再看 Mirage,將一個偽裝的服務器一起放到沙箱里,就顯得異常好用:
import { makeServer } from "server.js";
import { mount } from '@vue/test-utils';
import Todos from '@/components/Todos.vue';
let server;
beforeEach(() => {
server = makeServer({ environment: "test" });
});
it("Show todos from our server", () => {
server.create("todo", { id: 1, text: "Buy Onion" })
const wrapper = mount(Todos)
await waitFor(wrapper, '[data-test-id="1"]')
expect(wrapper.find('[data-test-id="1"]').text()).toBe("Buy Onion")
});
afterEach(() => {
server.shutdown();
});
引入 Mirage 后,只要記得在測試前后開關 mock 服務即可,其他的代碼與常規的 vue 單元測試無異。還有,Mirage 支持增刪改查呀!不僅是 unit 測試,甚至可以用在本地的 e2e 測試上:
it("shows the todos from our server", () => {
server.create("todo", { id: 1, text: "Buy Onion" })
cy.visit("/");
cy.get('[data-test-id="1"]')
.should("eq", "Buy Onion");
// CRUD operations...
});
其他的一些沙箱環境,如 storybook,有時也需要一些 api 交互,代理到 mock 服務器上還是耦合太高了。試試 Mirage, 你就能設計更簡潔的 stories 了。
小結
這期內容很少,介紹了一款國內還很小眾的 mock 工具——mirage.js。也沒啥好總結的,開發工具嘛就是用來提高生產效率的,讓你編碼舒服一點。當然,學習新工具也意味著增加新的認知成本,難點不在工具本身,而在于統一認識上。如果工作只是“撞鐘”或者本人已經“脫產”了,那這類工具自然也沒啥意義了。