Mirage JS 深入探討:了解工廠、夾具和序列化程序(第 2 部分)
已發表: 2022-03-10在本系列的上一篇文章中,我們了解了與 Mirage 相關的模型和關聯。 我解釋說,模型允許我們創建動態模擬數據,當 Mirage 向我們的模擬端點發出請求時,它將提供給我們的應用程序。 在本文中,我們將介紹 Mirage 的其他三個功能,這些功能可以實現更快速的 API 模擬。 讓我們潛入水中!
注意:我強烈建議您閱讀我的前兩篇文章,如果您還沒有真正掌握這裡將要討論的內容。 但是,您仍然可以在必要時繼續跟進並參考以前的文章。
- 使用 Mirage JS 和 Vue 設置 API 模擬
- Mirage JS 模型和關聯
工廠
在之前的文章中,我解釋瞭如何使用 Mirage JS 模擬後端 API,現在假設我們正在模擬 Mirage 中的產品資源。 為此,我們將創建一個路由處理程序,負責攔截對特定端點的請求,在這種情況下,端點是api/products
。 我們創建的路由處理程序將返回所有產品。 下面是在 Mirage 中實現此目的的代碼:
import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },
上面的輸出將是:
{ "products": [] }
我們從上面的輸出中看到產品資源是空的。 然而,這是意料之中的,因為我們還沒有創建任何記錄。
專業提示: Mirage 提供了傳統 API 端點所需的簡寫。 所以上面的路由處理程序也可以短如: this.get('/products')
。
讓我們使用Server
實例上的seeds
方法創建要存儲在 Mirage 數據庫中的product
模型記錄:
seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },
輸出:
{ "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }
正如您在上面看到的,當我們的前端應用程序向/api/products
發出請求時,它將返回seeds
方法中定義的產品集合。
使用seeds
方法播種 Mirage 的數據庫是一個步驟,無需手動將每個條目創建為對象。 但是,使用上述模式創建 1000 條(或 100 萬條)新產品記錄是不切實際的。 因此需要工廠。
工廠解釋
工廠是創建新數據庫記錄的更快方法。 它們使我們能夠快速創建特定模型的多條記錄,並將變體存儲在 Mirage JS 數據庫中。
工廠也是可以輕鬆生成逼真數據的對象,而無需單獨播種這些數據。 工廠更多是用於創建模型記錄的配方或藍圖。
創建工廠
讓我們通過創建一個工廠來檢查一下。 我們將創建的工廠將用作在 Mirage JS 數據庫中創建新產品的藍圖。
import { Factory } from 'miragejs' new Server({ // including the model definition for a better understanding of what's going on models: { product: Model }, factories: { product: Factory.extend({}) } })
從上面,您會看到我們向我們的Server
實例添加了一個factories
屬性,並在其中定義了另一個屬性,按照慣例,該屬性與我們要為其創建工廠的模型同名,在這種情況下,該模型是product
型號。 上面的代碼片段描述了在 Mirage JS 中創建工廠時將遵循的模式。
雖然我們有一個product
模型的工廠,但我們真的沒有給它添加屬性。 工廠的屬性可以是簡單的類型,如字符串、布爾值或數字,也可以是返回動態數據的函數,如下面的新產品工廠的完整實現所示:
import { Server, Model, Factory } from 'miragejs' new Server({ models: { product: Model }, factories: { product: Factory.extend({ name(i) { // i is the index of the record which will be auto incremented by Mirage JS return `Awesome Product ${i}`; // Awesome Product 1, Awesome Product 2, etc. }, price() { let minPrice = 20; let maxPrice = 2000; let randomPrice = Math.floor(Math.random() * (maxPrice - minPrice + 1)) + minPrice; return `$ ${randomPrice}`; }, category() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; }, rating() { let minRating = 0 let maxRating = 5 return Math.floor(Math.random() * (maxRating - minRating + 1)) + minRating; }, }), }, })
在上面的代碼片段中,我們通過Math.random
指定了一些 javascript 邏輯,以在每次使用工廠創建新產品記錄時創建動態數據。 這顯示了工廠的實力和靈活性。
讓我們利用上面定義的工廠創建一個產品。 為此,我們調用server.create
並將模型名稱 ( product
) 作為字符串傳入。 然後,Mirage 將使用我們定義的產品工廠創建產品的新記錄。 為此,您需要的代碼如下:
new Server({ seeds(server) { server.create("product") } })
專業提示:您可以運行console.log(server.db.dump())
來查看 Mirage 數據庫中的記錄。
在 Mirage 數據庫中創建並存儲了一條類似於以下的新記錄。
{ "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }
覆蓋工廠
我們可以通過顯式傳遞工廠提供的一些或更多值來覆蓋它們,如下所示:
server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })
結果記錄將類似於:
{ "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }
創建列表
有了工廠,我們可以在服務器對像上使用另一種方法createList
。 此方法允許通過傳入模型名稱和要創建的記錄數來創建特定模型的多個記錄。 下面是它的用法:
server.createList("product", 10)
要么
server.createList("product", 1000)
正如您將看到的,上面的createList
方法有兩個參數:模型名稱作為字符串和一個非零正整數,表示要創建的記錄數。 所以從上面,我們剛剛創建了 500 條產品記錄! 此模式對於 UI 測試很有用,您將在本系列的後續文章中看到。
夾具
在軟件測試中,測試夾具或夾具是一組或一組對象的狀態,用作運行測試的基線。 夾具的主要目的是確保測試環境是眾所周知的,以使結果可重複。
Mirage 允許您創建固定裝置並使用它們為您的數據庫播種初始數據。
注意:建議您使用工廠 10 次中的 9 次,因為它們使您的模擬更易於維護。
創建夾具
讓我們創建一個簡單的夾具來將數據加載到我們的數據庫中:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
以上數據作為 Mirage 的初始數據自動加載到數據庫中。 但是,如果您定義了種子函數,Mirage 將忽略您的固定裝置,並假設您打算覆蓋它,而是使用工廠來為您的數據播種。
與工廠結合的固定裝置
Mirage 為您提供了在 Factory 旁邊使用 Fixtures 的條件。 您可以通過調用server.loadFixtures()
來實現。 例如:
fixtures: { products: [ { id: 1, name: "iPhone 7" }, { id: 2, name: "Smart TV" }, { id: 3, name: "Pressing Iron" }, ], }, seeds(server) { // Permits both fixtures and factories to live side by side server.loadFixtures() server.create("product") },
夾具文件
理想情況下,您希望在server.js
的單獨文件中創建您的固定裝置並導入它。 例如,您可以創建一個名為fixtures
的目錄並在其中創建products.js
。 在products.js
添加:
// <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];
然後在server.js
中導入並使用 products 夾具,如下所示:
import products from './fixtures/products'; fixtures: { products, },
我正在使用 ES6 屬性簡寫,以便將導入的 products 數組分配給 fixtures 對象的products
屬性。
值得一提的是,Mirage JS 在測試期間會忽略固定裝置,除非您明確告訴它不要使用server.loadFixtures()
工廠與固定裝置
在我看來,你應該避免使用夾具,除非你有一個比工廠更適合的特定用例。 固定裝置往往更冗長,而工廠更快並且涉及更少的擊鍵。
序列化器
返回預期到前端的 JSON 有效負載非常重要,因此是serializers 。
序列化程序是一個對象,負責將路由處理程序返回的 **Model** 或 **Collection** 轉換為按照前端應用程序期望的方式格式化的 JSON 有效負載。
幻影文檔
讓我們以這個路由處理程序為例:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
序列化器負責將響應轉換為如下內容:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
Mirage JS 內置序列化程序
要使用 Mirage JS 序列化程序,您必須選擇從哪個內置序列化程序開始。 這個決定會受到後端最終發送到前端應用程序的 JSON 類型的影響。 Mirage 包含在以下序列化程序中:
-
JSONAPISerializer
此序列化程序遵循 JSON:API 規範。 -
ActiveModelSerializer
該序列化程序旨在模仿類似於使用 active_model_serializer gem 構建的 Rails API 的 API。 -
RestSerializer
RestSerializer
是 Mirage JS 用於其他常見 API 的“包羅萬象”序列化程序。
序列化器定義
要定義序列化,請從miragejs
導入適當的序列化程序,例如RestSerializer
,如下所示:
import { Server, RestSerializer } from "miragejs"
然後在Server
實例中:
new Server({ serializers: { application: RestSerializer, }, })
Mirage JS 默認使用RestSerializer
。 所以顯式設置它是多餘的。 上面的代碼片段用於示例目的。
讓我們在上面定義的同一路由處理程序上查看JSONAPISerializer
和ActiveModelSerializer
的輸出
JSONAPISerializer
import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })
輸出:
{ "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }
ActiveModelSerializer
要查看 ActiveModelSerializer 的工作情況,我會將產品工廠中的category
聲明修改為:
productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },
我所做的只是將屬性的名稱更改為productCategory
以顯示序列化程序將如何處理它。
然後,我們像這樣定義ActiveModelSerializer
序列化器:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
序列化程序將返回的 JSON 轉換為:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
您會注意到productCategory
已轉換為product_category
,它符合 Ruby 生態系統的 active_model_serializer gem。
自定義序列化器
Mirage 提供了自定義序列化程序的能力。 假設您的應用程序要求您的屬性名稱為駝峰式,您可以覆蓋RestSerializer
來實現這一點。 我們將使用lodash
實用程序庫:
import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },
這應該產生以下形式的 JSON:
{ "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }
包起來
你做到了! 希望您通過本文對 Mirage 有更深入的了解,並且您還了解瞭如何利用工廠、固定裝置和序列化程序使您能夠使用 Mirage 創建更多類似於生產的 API 模擬。
- 第 1 部分:了解 Mirage JS 模型和關聯
- 第 2 部分:了解工廠、夾具和序列化程序
- 第 3 部分:了解時序、響應和直通
- 第 4 部分:使用 Mirage JS 和 Cypress 進行 UI 測試