如果您是前端工程师,您可能遇到过这样的情况:您被要求在服务后端部分的 API 之前开始实现某个功能功能存在。工程师通常会转向模拟来实现并行开发(这意味着该功能的前端和后端部分是并行开发的)。
然而,Mocking 可能会带来一些缺点。第一个也是最明显的一点是模拟可能会偏离实际实现,导致它们不可靠。第二个问题是模拟通常很冗长;对于包含大量数据的模拟,可能不清楚某个模拟响应实际上在模拟什么。
以下数据是您可能在代码库中找到的一些数据的示例:
type Order = { orderId: string; customerInfo: CustomerInfo; // omitted these types for brevity orderDate: string; items: OrderItem[]; paymentInfo: PaymentInfo; subtotal: number; shippingCost: number; tax: number; totalAmount: number; status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled'; trackingNumber: string | null; }; const mockOrders: Order[] = [ { orderId: "ORD-2024-001", customerInfo: { id: "CUST-1234", name: "Alice Johnson", email: "alice.j@email.com", shippingAddress: { street: "123 Pine Street", city: "Portland", state: "OR", zipCode: "97201", country: "USA" } }, orderDate: "2024-03-15T14:30:00Z", items: [ { productId: "PROD-789", name: "Organic Cotton T-Shirt", quantity: 2, pricePerUnit: 29.99, color: "Navy", size: "M" }, { productId: "PROD-456", name: "Recycled Canvas Tote", quantity: 1, pricePerUnit: 35.00, color: "Natural" } ], paymentInfo: { method: "credit_card", status: "completed", transactionId: "TXN-88776655" }, subtotal: 94.98, shippingCost: 5.99, tax: 9.50, totalAmount: 110.47, status: "shipped", trackingNumber: "1Z999AA1234567890" }, // Imagine more objects here, with various values changed... ];
我每天使用的数据看起来很像这样。订单数组或某种以客户为中心的信息,具有嵌套值,可帮助填充详细说明各种信息的表格、弹出窗口和卡片。
作为一名负责维护严重依赖此类模拟的应用程序的工程师,您可能会问“响应模拟中的这个特定对象是什么?”。我经常发现自己滚动浏览数百个示例,就像上面的示例一样,但不确定每个对象的用途是什么。
随着我对自己作为工程师的身份越来越有信心,我给自己下定决心要解决上述问题;如果每个模拟都可以更轻松地展示其目的怎么办?如果工程师只需要编写他们打算模拟的行怎么办?
在使用一些代码和一个名为 Zod 的库时,我发现了以下称为 parse 的方法,它尝试根据已知类型验证传入数据:
const stringSchema = z.string(); stringSchema.parse("fish"); // => returns "fish" stringSchema.parse(12); // throws error
这是一个灵光乍现的时刻; Zod 文档中的这个小例子正是我一直在寻找的!如果解析方法可以接受一个值并返回它,那么如果我传入一个值,我就会得到它。我也已经知道我可以为 Zod 模式定义默认值。如果传递一个空对象将返回一个完整的对象及其值怎么办?你瞧,确实如此;我可以在 Zod 模式上定义默认值,并返回默认值:
const UserSchema = z.object({ id: z.string().default('1'), name: z.string().default('Craig R Broughton'), settings: z.object({ theme: z.enum(['light', 'dark']), notifications: z.boolean() }).default({ theme: 'dark', notifications: true, }) }); const user = UserSchema.parse({}) // returns a full user object
现在我有了一种生成对象的方法,但它仍然不是我想要的。我真正想要的是一种仅写下我正在“嘲笑”的确切行的方法。一个简单的解决方案可能如下所示:
const UserSchema = z.object({ id: z.string().default('1'), name: z.string().default('Craig R Broughton'), settings: z.object({ theme: z.enum(['light', 'dark']), notifications: z.boolean() }).default({ theme: 'dark', notifications: true, }) }); const user = UserSchema.parse({}) const overridenUser = {...user, ...{ name: "My new name", settings: {}, // I would need to write every key:value for settings :( } satisfies Partial<z.infer userschema>>} // overrides the base object </z.infer>
然而这也有其自身的缺陷;如果我希望覆盖的值本身就是一个对象或数组怎么办?然后,我必须手动输入该功能之前所需的每一行才能继续工作并按预期被嘲笑,这违背了我们正在进行的解决方案的目的。
很长一段时间以来,这就是我所得到的,直到最近我再次尝试改进上述内容。第一步是定义“API”;我希望我的用户如何与此功能交互?
type Order = { orderId: string; customerInfo: CustomerInfo; // omitted these types for brevity orderDate: string; items: OrderItem[]; paymentInfo: PaymentInfo; subtotal: number; shippingCost: number; tax: number; totalAmount: number; status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled'; trackingNumber: string | null; }; const mockOrders: Order[] = [ { orderId: "ORD-2024-001", customerInfo: { id: "CUST-1234", name: "Alice Johnson", email: "alice.j@email.com", shippingAddress: { street: "123 Pine Street", city: "Portland", state: "OR", zipCode: "97201", country: "USA" } }, orderDate: "2024-03-15T14:30:00Z", items: [ { productId: "PROD-789", name: "Organic Cotton T-Shirt", quantity: 2, pricePerUnit: 29.99, color: "Navy", size: "M" }, { productId: "PROD-456", name: "Recycled Canvas Tote", quantity: 1, pricePerUnit: 35.00, color: "Natural" } ], paymentInfo: { method: "credit_card", status: "completed", transactionId: "TXN-88776655" }, subtotal: 94.98, shippingCost: 5.99, tax: 9.50, totalAmount: 110.47, status: "shipped", trackingNumber: "1Z999AA1234567890" }, // Imagine more objects here, with various values changed... ];
上面的 API 将允许用户指定他们选择的模式,然后提供适当的覆盖并返回一个用户对象!当然,我们希望正确考虑数组以及单个对象。为此,对传入的覆盖类型进行简单的类型检查就足够了:
const stringSchema = z.string(); stringSchema.parse("fish"); // => returns "fish" stringSchema.parse(12); // throws error
上面的代码实际上与之前相同,但是现在它在内部封装了解析,因此用户不必手动执行此操作或了解有关 Zods 解析方法的详细信息。正如您通过阅读包含的 if/else 语句可能猜到的那样,我们还通过使用递归构建器函数来解决嵌套对象和数组的保存问题,该函数解析每个值并返回 Zod 模式中指定的默认值。 🎜>
上面的内容有点让人费解,但结果是用户可以执行以下操作:
const UserSchema = z.object({ id: z.string().default('1'), name: z.string().default('Craig R Broughton'), settings: z.object({ theme: z.enum(['light', 'dark']), notifications: z.boolean() }).default({ theme: 'dark', notifications: true, }) }); const user = UserSchema.parse({}) // returns a full user object当向构建器提供preserveNestedDefaults配置选项时,用户可以保留嵌套对象或数组中的键值对!这解决了用户覆盖不是像字符串这样的原始类型的键的问题,而是一种更复杂的类型,并保留所有值减去我们选择覆盖的值。
这已经是一篇相当长的文章了,所以让我们以我们所有努力工作的结果来结束吧。让我们回顾一下第一个模拟,以及如何使用 zodObjectBuilder 编写它。首先让我们定义我们的类型和默认值,并将结果模式传递到 zodObjectBuilder 中:
const UserSchema = z.object({ id: z.string().default('1'), name: z.string().default('Craig R Broughton'), settings: z.object({ theme: z.enum(['light', 'dark']), notifications: z.boolean() }).default({ theme: 'dark', notifications: true, }) }); const user = UserSchema.parse({}) const overridenUser = {...user, ...{ name: "My new name", settings: {}, // I would need to write every key:value for settings :( } satisfies Partial<z.infer userschema>>} // overrides the base object </z.infer>上述实现将使用所有默认值返回单个对象!但我们可以做得更好,我们现在可以(借助一些重载定义和内部解析)创建对象数组,非常适合模拟 API 响应的用例:
const UserSchema = z.object({ id: z.string().default('1'), name: z.string().default('Craig R Broughton'), settings: z.object({ theme: z.enum(['light', 'dark']), notifications: z.boolean() }).default({ theme: 'dark', notifications: true, }) }); const user = zodObjectBuilder({ schema: UserSchema, overrides: { name: 'My new name', settings: { theme: 'dark' } } // setting is missing the notifications theme :( }); // returns a full user object with the overrides上面输出的订单数组将是完整的默认值,并覆盖交付状态!希望这演示了 zodObjectBuilder 函数如何最大限度地减少基于可靠的类型安全模式创建新模拟所需的工作。
通过这个小演示,我们已经完成了第一篇文章的结尾:) 我希望您喜欢阅读这段探索改进模拟的旅程。 zodObjectBuilder 仍在构建中,但它很好地满足了我最小化模拟对象的需求。如果您想尝试当前版本,可以在 https://www.npmjs.com/package/@crbroughton/ts-utils 找到它,其中包含该功能。
以上是我如何尝试用 Zod 改进模拟的详细内容。更多信息请关注PHP中文网其他相关文章!

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境