建立應用程式時,模擬資料對於測試、開發和原型設計非常寶貴。借助 Zod 強大的模式驗證和 Faker 的資料產生功能,我們可以創建一個強大的助手來為任何 Zod 模式產生真實的、符合模式的模擬資料。
在本指南中,我們將建立一個輔助函數generateMockDataFromSchema,它接受 Zod 模式並傳回與該模式的結構和驗證規則相符的模擬資料。讓我們一步步深入吧!
在開始編碼之前,讓我們先討論為什麼 Zod 和 Faker 非常適合這項任務:
Zod:提供了一種健全、類型安全的方法來定義 TypeScript 中的資料模式。其架構驗證功能可確保我們的模擬資料符合特定規則,例如電子郵件格式、UUID 或最小/最大。
Faker:產生真實的隨機數據,例如姓名、日期、電子郵件和 URL。當我們需要類似於真實世界場景的模擬數據時,這特別有用,使其非常適合測試和演示目的。
結合 Zod 和 Faker 使我們能夠創建既真實又符合架構的模擬資料。
我們解決方案的核心是generateMockDataFromSchema輔助函數,它可以解釋Zod模式並產生匹配的模擬資料。此函數處理各種資料類型(字串、數字、陣列、物件)並遵守每種模式類型內的驗證約束。讓我們來探索一下它是如何建構的。
generateMockDataFromSchema 函式接受兩個參數:
這是函數,分為每個部分來解釋不同模式類型的處理。
import { ZodSchema, ZodObject, ZodString, ZodNumber, ZodBoolean, ZodArray, ZodOptional, ZodNullable, ZodTypeAny, ZodStringCheck, } from "zod"; import { faker } from "@faker-js/faker"; import { z } from "zod"; const handleStringCheck = (check: ZodStringCheck) => { switch (check.kind) { case "date": return faker.date.recent().toISOString(); case "url": return faker.internet.url(); case "email": return faker.internet.email(); case "uuid": case "cuid": case "nanoid": case "cuid2": case "ulid": return crypto.randomUUID(); case "emoji": return faker.internet.emoji(); default: return faker.lorem.word(); } }; type GeneratorPrimitiveOptions = { array?: { min?: number; max?: number; }; optional?: { probability?: number; }; }; const getArrayLength = (options?: GeneratorPrimitiveOptions) => { return faker.number.int({ min: options?.array?.min || 1, max: options?.array?.max || 10, }); }; export function generateTestDataFromSchema<T>( schema: ZodSchema<T>, options?: GeneratorPrimitiveOptions ): T { if (schema instanceof ZodString) { const check = schema._def.checks.find((check) => handleStringCheck(check)); if (check) { return handleStringCheck(check) as T; } return faker.lorem.word() as T; } if (schema instanceof ZodNumber) { return faker.number.int() as T; } if (schema instanceof ZodBoolean) { return faker.datatype.boolean() as T; } if (schema instanceof ZodArray) { const arraySchema = schema.element; const length = getArrayLength(options); return Array.from({ length }).map(() => generateTestDataFromSchema(arraySchema) ) as T; } if (schema instanceof ZodOptional || schema instanceof ZodNullable) { const probability = options?.optional?.probability || 0.5; return ( Math.random() > probability ? generateTestDataFromSchema(schema.unwrap()) : null ) as T; } if (schema instanceof ZodObject) { const shape = schema.shape; const result: any = {}; for (const key in shape) { result[key] = generateTestDataFromSchema(shape[key] as ZodTypeAny); } return result as T; } throw new Error("Unsupported schema type", { cause: schema, }); }
在generateMockDataFromSchema中,每種Zod模式類型(如ZodString、ZodNumber等)都會進行不同的處理,以適應其獨特的需求。讓我們來看看每種類型。
對於 ZodString,我們需要考慮任何特定的檢查,例如電子郵件、url 或 uuid。這就是我們的輔助函數 handleStringCheck 的用武之地。它檢查字串模式,如果存在任何檢查,則傳回相關的模擬值(例如,電子郵件為電子郵件,網址為 URL)。如果沒有找到特定的檢查,則預設會產生隨機單字。
const handleStringCheck = (check: ZodStringCheck) => { switch (check.kind) { case "date": return faker.date.recent().toISOString(); case "url": return faker.internet.url(); case "email": return faker.internet.email(); case "uuid": case "cuid": case "nanoid": case "cuid2": case "ulid": return crypto.randomUUID(); case "emoji": return faker.internet.emoji(); default: return faker.lorem.word(); } };
在generateMockDataFromSchema中,我們使用這個助手為帶有檢查的字串欄位產生資料。
對於 ZodNumber,我們使用 Faker 的 faker.number.int() 方法來產生整數。如果在架構中定義了最小值和最大值,則可以進一步自訂此部分以處理最小值和最大值。
if (schema instanceof ZodNumber) { return faker.number.int() as T; }
對於布林值,Faker 提供了一個簡單的 faker.datatype.boolean() 函數來隨機產生 true 或 false 值。
if (schema instanceof ZodBoolean) { return faker.datatype.boolean() as T; }
在處理 ZodArray 時,我們為陣列中的每個元素遞歸產生模擬資料。我們還允許使用 options 參數自訂數組長度。
要產生數組,我們首先使用 getArrayLength 來確定長度,這是一個輔助函數,用於檢查選項中的最小和最大長度。對於每個陣列元素,都會遞歸呼叫generateMockDataFromSchema,確保陣列中的巢狀模式也被處理。
type GeneratorPrimitiveOptions = { array?: { min?: number; max?: number; }; optional?: { probability?: number; }; }; if (schema instanceof ZodOptional || schema instanceof ZodNullable) { const probability = options?.optional?.probability || 0.5; return ( Math.random() > probability ? generateTestDataFromSchema(schema.unwrap()) : null ) as T; } const getArrayLength = (options?: GeneratorPrimitiveOptions) => { return faker.number.int({ min: options?.array?.min || 1, max: options?.array?.max || 10, }); };
可選欄位和可為空欄位是透過隨機決定是否將它們包含在輸出中來處理的。 options.Optional.probability 設定允許我們控制這個機率。如果產生了一個字段,它會遞歸呼叫generateMockDataFromSchema來取得內部模式。
if (schema instanceof ZodOptional || schema instanceof ZodNullable) { const shouldGenerate = Math.random() > (options?.optional?.probability || 0.5); return shouldGenerate ? generateMockDataFromSchema(schema.unwrap(), options) : null; }
對於 ZodObject,我們迭代每個鍵值對並遞歸地為每個欄位產生資料。這種方法支援深度嵌套的對象,使其高度靈活。
if (schema instanceof ZodObject) { const shape = schema.shape; const result: any = {}; for (const key in shape) { result[key] = generateMockDataFromSchema(shape[key] as ZodTypeAny, options); } return result as T; }
generateMockDataFromSchema 就位後,讓我們來看看它的實際效果。這是一個範例架構 UserSchema,具有不同的類型、可選欄位和巢狀數組。
import { ZodSchema, ZodObject, ZodString, ZodNumber, ZodBoolean, ZodArray, ZodOptional, ZodNullable, ZodTypeAny, ZodStringCheck, } from "zod"; import { faker } from "@faker-js/faker"; import { z } from "zod"; const handleStringCheck = (check: ZodStringCheck) => { switch (check.kind) { case "date": return faker.date.recent().toISOString(); case "url": return faker.internet.url(); case "email": return faker.internet.email(); case "uuid": case "cuid": case "nanoid": case "cuid2": case "ulid": return crypto.randomUUID(); case "emoji": return faker.internet.emoji(); default: return faker.lorem.word(); } }; type GeneratorPrimitiveOptions = { array?: { min?: number; max?: number; }; optional?: { probability?: number; }; }; const getArrayLength = (options?: GeneratorPrimitiveOptions) => { return faker.number.int({ min: options?.array?.min || 1, max: options?.array?.max || 10, }); }; export function generateTestDataFromSchema<T>( schema: ZodSchema<T>, options?: GeneratorPrimitiveOptions ): T { if (schema instanceof ZodString) { const check = schema._def.checks.find((check) => handleStringCheck(check)); if (check) { return handleStringCheck(check) as T; } return faker.lorem.word() as T; } if (schema instanceof ZodNumber) { return faker.number.int() as T; } if (schema instanceof ZodBoolean) { return faker.datatype.boolean() as T; } if (schema instanceof ZodArray) { const arraySchema = schema.element; const length = getArrayLength(options); return Array.from({ length }).map(() => generateTestDataFromSchema(arraySchema) ) as T; } if (schema instanceof ZodOptional || schema instanceof ZodNullable) { const probability = options?.optional?.probability || 0.5; return ( Math.random() > probability ? generateTestDataFromSchema(schema.unwrap()) : null ) as T; } if (schema instanceof ZodObject) { const shape = schema.shape; const result: any = {}; for (const key in shape) { result[key] = generateTestDataFromSchema(shape[key] as ZodTypeAny); } return result as T; } throw new Error("Unsupported schema type", { cause: schema, }); }
generateMockDataFromSchema 函數也接受可選的 options 參數來自訂陣列長度和可選欄位行為。以下是如何使用這些選項的範例:
const handleStringCheck = (check: ZodStringCheck) => { switch (check.kind) { case "date": return faker.date.recent().toISOString(); case "url": return faker.internet.url(); case "email": return faker.internet.email(); case "uuid": case "cuid": case "nanoid": case "cuid2": case "ulid": return crypto.randomUUID(); case "emoji": return faker.internet.emoji(); default: return faker.lorem.word(); } };
這將確保數組字段的長度在 2 到 5 之間,並且可選字段的生成機率為 70%。
要確認generateMockDataFromSchema 按預期運作,請為不同的架構配置建立單元測試。以下是數組模式測試的範例:
if (schema instanceof ZodNumber) { return faker.number.int() as T; }
透過為各種模式類型和配置編寫測試,您可以確保輔助函數在不同場景下正確運行。
透過結合 Zod 和 Faker,我們創建了一個針對 TypeScript 專案量身定制的強大、可重複使用的類比資料產生器。測試不同場景並查看實際數據的能力對於快速開發和品質測試來說非常寶貴。
以上是使用 Zod 和 Faker 建立用於產生模擬資料的 TypeScript 助手的詳細內容。更多資訊請關注PHP中文網其他相關文章!