引言

最近閱讀 Mocking | GraphQL Tools 看裡面的如何實現 mocking 後端數據。然後看到一篇介紹背景的文章,於是打算譯一下。介紹使用 GraphQL 的靜態類型生成假數據。

(注意:博客的概念是準確的,但是一些代碼樣例不不是演示的最新的使用模式。閱讀之後,查看 )Mocking | GraphQL Tools(注意:博客的概念是準確的,但是一些代碼樣例不不是演示的最新的使用模式。閱讀之後,查看 graphql-tools docs 去看最新的 mocking是如何用的 )

你認為mocking 你的後端是一個乏味的工作嗎?如果你這麼認為,看了這篇文章,很可能會改變你的想法...

mocking 是創建假的組件版本的實踐,這樣你可以獨立的開發和測試你的應用的其他部分。

mocking 你的後端是一個非常好的方式去快速構建一個前端的原型,讓你能測試前端而不用開啟任何服務。 API mocking 很有用,在 Google 上搜索,會返回很多付費產品和服務,承諾能幫助你。

悲傷的是,我認為這些解決方案裡面沒有一個可以它變的本應該的那麼容易。事實證明,那是因為你使用了錯誤的技術。

上個星期在 Building Apollo 文章里,我寫了工具集,最後能搭建起 Apollo Server。Mocking 是我們能 demo 的第一個事情,我希望你會喜歡它。

為什麼 mock ?

mocking 後端的數據非常有用有兩個主要的原因:

  1. 它你能讓你在還沒有一個已經工作的後端服務的時候,開始開發前端特性。這是對很多前端和後端組件並行開發的項目來說是非常重要的。

2. 它讓你可以在本地運行測試,而不用連接到真實的後端, 這樣更快更安全。隨著你的代碼庫的增長,你的應用變的越來越複雜,開啟所有服務端的架構去運行測試是不可行的。

既然 mocking 後端 API 有這麼多顯而易見多好處,為什麼不是每個人都這麼做呢?我認為是因為它耗費麻煩大於它帶來都好處。

為什麼 mocking 後端很難?

假設你的後端是 REST API ,在瀏覽器上通過http 請求被調用。一部分人負責後端,一部分人負責前端。後端代碼實際決定每個 REST 入口返回數據的結構,但是mocking不得不寫在前端的代碼里。意味著每次後端變化,mocking 的代碼會被破壞,除非前後端同時修改。更糟糕的是,如果你在做你的前端測試用一個不是最新的 mock 代碼,你的測試會通過,但是代碼卻並不工作。所以最好的選擇不是保持你依賴後端結構而寫的 mock 代碼最新,而是不去mock REST API 或者後端自己mocking 自己,後端自己mocking 自己就需要代碼都在同一個地方。後面的選擇可能會容易些,但是會減慢你的速度。

我常常聽到大家不去在項目裡面 mock 後端的數據第二個原因,是因為它花時間去搭建:首先你不得不在你的獲取數據層包含額外的邏輯層,然後你把 mocking 打開和關閉,和第二點是你不得不描述 mock data 的樣子。對任何微不足道道 API,需要很多枯燥的工作。

mocking 後端數據很很難的這個兩個原因,都有一樣的潛在原因:沒有標準的REST API 描述是機器可以識別的格式 和 對mocking來說非常必要的信息,需要被前端和後端都使用。有一些API 的描述標準,比如Swagger ,但是不包含你所需要的全部信息,處理和維護起來也很繁瑣。mocking 有很多工作量,除非你想花很多錢去購買服務或者甚至產品。

實際上,我應該說mocking需要很多工作量,是因為需要一個東西去改變 API 的思考方式。這就是我們說的 GraphQL

在類型系統系 Mocking 很容易

GraphQL 讓 mocking 變的容易,因為它讓你給後端定義了類型系統。類型系統可以在前端和後端之前被分享,它包含了全部必要信息,讓 mocking 非常快和非常方便。你表面上沒有更多的理由不去在開發和測試的時候用 mocking 。

如果你還不了解 GraphQL我推薦你先開始讀 GraphQL 介紹。否則文章的剩餘部分會看起來像魔法。

如下就是如何開始輕鬆用我們為 Apollo 構建的 GraphQL tools為任何有效的的 GraphQL 查詢去 mocking 你的後端。 code link

// > npm install graphql-tools
import { mockServer } from graphql-tools;
import schema from ./mySchema.graphql;

const myMockServer = mockServer(schema);
myMockServer.query(`{
allUsers: {
id
name
}
}`);

// returns
// {
// data: {
// allUsers:[
// { id: ee5ae76d-9b91-4270-a007-fad2054e2e75, name: lorem ipsum },
// { id: ca5c182b-99a8-4391-b4b4-4a20bd7cb13a, name: quis ut }
// ]
// }
// }

每個 GraphQL 服務需要一個 schema ,你不需要為 mocking 寫額外的代碼。上面的查詢是你組件用來獲取數據的查詢,但是也不只是你為 mocking 而寫的。不要計算引入的聲明的個數,它只需要一行代碼去 mock 全部的後端。

相對於大部分 REST APIs, mocking 意味著 URL 轉換,在每個入口處返回定製結構的數據。它需要很多代碼去 mock 一個簡單入口,去返回一些實際查看的數據。而用 GraphQL,結構是被定義在 query 和schema 裡面,query 和 schema 就為用簡單的一行代碼去 mock 服務端提供了足夠的信息。

提到的那一行代碼,是你發送的任何有效的 GraphQL query 查詢所需要的全部嗎?不只是一些有效的 query,任何有效的 query。非常棒,對不對?

定製 mocking data

在上面的例子裡面,mock 服務會返回的完全隨機的 IDs 和 strings 每次你查詢的時候。如果你只是開始構建應用,只是想看你的UI 代碼實際顯示什麼東西,這很可能已經足夠好了。但是隨著你開始調整你的設計,你想 mock server 去測試你的組件邏輯。你很可能需要更多的真實數據。

很幸運,它幾乎不費任何更多的努力。因為定製 mock data 就是 Apollo mocking 工具的工作。你可以定製任何返回的虛擬數據。它讓你做下面的事情,或則更多:code link

// customize mocking per type (i.e. Integer, Float, String)
mockServer(schema, {
Int: () => 6,
Float: () => 22.1,
String: () => Hello,
});

// customize mocking per field in the schema (i.e. for Person.name and Person.age)
mockServer(schema, {
Person: () => ({
name: casual.name,
age: () => casual.integer(0,120),
})
});

// mock lists of specific or random length( and lists of lists of lists …)
mockServer(schema, {
Person: () => {
// a list of length between 2 and 6
friends: () => new MockList([2,6]),
// a list of three lists of two items: [[1, 1], [2, 2], [3, 3]]
listOfLists: () => new MockList(3, () => new MockList(2)),
},
});

// customize mocking of a field or type based on the query arguments
mockServer(schema, {
Person: () => {
// the number of friends in the list now depends on numPages
paginatedFriends: (o, { numPages }) => new MockList(numPages * PAGE_SIZE),
},
});

你可以為每個類型和每個 field 提供一個函數,調用後能自動生成 mock 數據。在 field 上mock 函數比在 types 上高的優先順序,但是它們合作的非常好。 field mock 函數只需要描繪 它們關心的對象的屬性,type mock 函數會填滿剩下的內容。

mock 函數其實是假的 GraphQL resolve 函數。這意味著在用 Graphql resolve 函數內部做的任何事情,mocking 的都可以做。如果你想這麼做,你可以全部後端都用它實現,我不是說你應該,而是說你可以。

我認為這個tool 真正強大的地方是它允許了幾乎任何複雜的定製。你可以真的非常快速的開始,然後在任何你需要的時候逐步增加你 mock 的複雜度。讓每一步都像凌波微步一樣。

但是說的夠多了,這有一個完整的例子:code link

import { mockServer, MockList } from graphql-tools;
import casual from casual-browserify;

// The GraphQL schema. Described in more detail here:
// https://medium.com/apollo-stack/the-apollo-server-bc68762e93b
const schema = `
type User {
id: ID!
name: String
lists: [List]
}
type List {
id: ID!
name: String
owner: User
incomplete_count: Int
tasks(completed: Boolean): [Task]
}
type Task {
id: ID!
text: String
completed: Boolean
list: List
}
type RootQuery {
user(id: ID): User
}
schema {
query: RootQuery
}
`;

// Mock functions are defined per type and return an
// object with some or all of the fields of that type.
// If a field on the object is a function, that function
// will be used to resolve the field if the query requests it.
const server = mockServer(schema, {
RootQuery: () => ({
user: (o, { id }) => ({ id }),
}),
List: () => ({
name: () => casual.word,
tasks: () => new MockList(4, (o, { completed }) => ({ completed })),
}),
Task: () => ({ text: casual.words(10) }),
User: () => ({ name: casual.name }),
});

mockServer.query(`
query tasksForUser{
user(id: 6) {
id
name
lists {
name
completeTasks: tasks(completed: true) {
completed
text
}
incompleteTasks: tasks(completed: false) {
completed
text
}
anyTasks: tasks {
completed
text
}
}
}
}`);

在線 demo + 你自己親自嘗試

為了看可操作的例子和看它生成的輸出。轉向 live demo 和多次點擊「播放」按鈕

你想自己倒騰下這個例子。你可以這樣— 它在github上。如果你對它如何工作感到好奇,或者想看我們為 Graphql 構建的其他工具,轉向 apollostack/graphql-tools

非常棒,對不對?通過使用類型系統,一切都變的可能。這還只是個開始, 我們搭建了 mocking 和真實世界的橋樑,隨著你給它添加更多的功能,可以讓你的 mock server 逐步轉向真實的 server。

如果你喜歡這個博客,也可能會對 Building Apollo 裡面的其他博客感興趣,我們每周發兩篇,關於我們工作和思考的東西。


推薦閱讀:
相关文章