在打字稿中编写可靠的测试可能是一项具有挑战性的任务。它通常涉及复杂的任务,例如mocking modules,stubbing API responses和spying on functions。
但是,通过使用工厂功能,我们可以在打字条和反应测试中实现一致性,可读性和键入安全性。
在本文中,我将演示如何使用简单的创建者功能生成工厂并加快测试写作和故事书创建过程。
以前的方法:很多临时内容
过去,我依靠在故事书或组件测试中构建许多小型工厂功能来使用。尽管这种方法使我能够为测试和故事书故事构建典型的对象,但它也引入了大量的审查和维护代码。
在每个新测试方案中,我都发现自己定义了新功能和类型,这增加了复杂性,并使在我们的代码库中的一致性变得更加困难。
寻找解决方案:NPM软件包
在搜索解决方案时,我探索了可以处理工厂逻辑的各种打字稿库。一个这样的图书馆就是fishery,它的设计非常精确。
但是,我意识到它提供的功能超过了用例所需的功能。因此,我决定创建自己的自定义工厂功能,适合我们的特定要求。
介绍makeFactory
:工厂创建者
这个称为makeFactory
的工厂创建器函数返回工厂功能,例如createUser
,可以调用以生成新对象。
它接受可以覆盖的默认对象参数,为不同方案提供了灵活性。例如,我们可以使用makeFactory<IUser>({ name: 'Jane', age: 40 })
的通用物来明确定义对象的类型。
这是makeFactory函数的实现:
import merge from "lodash/merge";
import cloneDeep from "lodash/cloneDeep";
export function makeFactory<T>(
defaultArgs: T
): (args?: RecursivePartial<T>) => T {
return (args = {}) => merge({}, cloneDeep(defaultArgs), args);
}
通过使用此模式,我们可以创建一个像createUser
这样的函数,该函数使用IUser
类型生成对象:
const createUser = makeFactory<IUser>({
name: "George Rosin",
type: "customer",
age: 25,
});
被调用时,它看起来像这样:
const USER_ADMIN = createUser({ type: 'admin' })
// { name: 'George Rosin', type: 'admin', age: 25 }
makeFactory
函数的参数定义了分配给从createUser
返回的对象的默认属性。传递给createUser
函数的任何属性都将覆盖默认属性,从而轻松地定义测试的不同数据方案。
故事书中的用法:加快事情的速度!
此工厂功能模式不仅简化了对象创建,还可以增强可读性。在故事书故事的背景下,我们可以轻松地定义不同的情况。这是一个快速示例:
// UserLabel.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import UserLabel, { IUserLabelProps } from "./UserLabel";
import createUser from "./createUser";
const meta: Meta<typeof UserLabel> = {
title: "UserLabel",
component: UserLabel,
};
export default meta;
type Story = StoryObj<typeof UserLabel>;
export const Admin: Story = {
args: {
user: createUser({ type: "admin" }),
},
};
export const User: Story = {
args: {
user: createUser({ type: "customer" }),
},
};
通过利用工厂功能createUser
,我们可以轻松地定义不同的方案,同时保持故事书故事中的可读性。
在组件测试中使用:更快的测试!
工厂功能的功率也扩展到了反应组件测试。就像故事书的故事中一样,我们可以明确定义场景之间的差异。这是一个例子:
// UserLabel.spec.tsx
import { render, screen } from "@testing-library/react";
import UserLabel, { IUserLabelProps } from "./UserLabel";
describe("<UserLabel />", () => {
it("renders an 'admin' label", () => {
const USER_ADMIN = createUser({ type: "admin" });
render(<UserLabel user={USER_ADMIN} />);
expect(screen.getByTestId("UserLabel")).toContain("George John - Admin");
});
it("renders a 'customer' label", () => {
const USER_CUSTOMER = createUser({ type: "customer" });
render(<UserLabel user={USER_CUSTOMER} />);
expect(screen.getByTestId("UserLabel")).toContain("George John - Customer");
});
});
使用这种方法,即使处理复杂的API模型或类型,测试也会变得更简单,更有效。
进一步:嵌套功能
在与嵌套对象处理复杂的接口类型时,使用其他工厂内的工厂也可能很有用。这是该模式的示例:
interface IAddress {
addressLine1: string;
state: string;
city: string;
postalCode: string;
}
interface IBusiness {
id: string;
name: string;
address: IAddress;
}
const createAddress = makeFactory<IAddress>({
addressLine1: '123 Broadway',
state: 'NY',
city: 'Chester',
postalCode: '10920'
});
const createBusiness = makeFactory<IBusiness>({
id: '123',
name: 'Pizza Inc.',
address: createAddress()
});
虽然上面的示例有些简单,但功能的模块化会产生可重复使用性,这确实可以有用。
此外,由于lodash
的merge
函数,我们可以通过部分值传递并覆盖嵌套的对象属性,例如:
const BUSINESS_PA = createBusiness({ address: { state: 'PA' }})
/**
* {
* id: '123',
* name: 'Pizza Inc.',
* address: {
* addressLine1: '123 Broadway',
* state: 'PA',
* city: 'Chester',
* postalCode: '10920'
* }
* }
*/
总之:做工厂ð
通过利用工厂功能,我们可以在打字稿和反应中以一致性,可读性和键入安全性创建可靠的测试。 makeFactory
模式提供了一种灵活且可维护的解决方案,用于生成键入对象,以加快开发人员的工作流程来编写测试和故事书故事。
奖金:使用chatgpt生成默认值
这是一个提示,我发现根据属性名称的不同:
来生成现实默认值ðä对于以下代码,为每个接口创建一个基于属性名称现实的键入值的键入对象。
interface IAddress {
addressLine1: string;
state: string;
city: string;
postalCode: string;
}
interface IBusiness {
id: string;
name: string;
address: IAddress;
}
这返回了Chatgpt的以下答案:
当然!这是每个接口的键入对象,具有现实的属性值:
const address: IAddress = {
addressLine1: "123 Main Street",
state: "California",
city: "Los Angeles",
postalCode: "90001",
};
const business: IBusiness = {
id: "123456789",
name: "ABC Company",
address: address,
};
ð不好!