工厂
这个软件包通过提供为您的实体/模型创建工厂的方法,使得测试更加容易. 灵感来自 工厂男孩 ++ python 软件包和 工厂女孩 👧.
您也可以查看这些项目:
安装
国家预防机制
npm install @adrien-may/factory --save-dev
雅伦
yarn add @adrien-may/factory --dev
使用量
本节简要概括了您使用此套件所能实现的目标.
工厂
声明
要申报工厂,必须提供:
- 适配器:ObjectAdapter, TypoormAdapter, ...
- 一个实体[可选的无打字机]:您正在构建的模型
- 要覆盖的字段( 它们的默认值或生成它们的方法)
适配器允许您坚持您的数据 。 如果你想通过打字机将数据保存在数据库中,可以使用 TypeormAdapter 。默认适配器是 ObjectAdapter 而不坚持任何东西。 您可以创建自己的适配器, 以便按您想要的方式坚持您的数据 .
import { Factory } from '@adrien-may/factory';
// Object/Entity to create
class User {
email: string;
role: string;
}
// Factories
class UserFactory extends Factory<User> {
entity = User;
attrs = {
email: '[email protected]',
role: 'basic',
};
// By default, adapter is ObjectAdapter
}
class AdminUserFactory extends Factory<User> {
entity = User;
attrs = {
email: '[email protected]',
role: 'admin',
};
}
// Usage
const adminFactory = new AdminUserFactory();
const admin1 = adminFactory.create();
const admins = adminFactory.createMany(5);
类型
import { Factory, TypeormFactory, TypeormAdapter } from '@adrien-may/factory';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
const dataSource = new DataSource({
type: 'sqlite',
database: ':memory:',
... // other typeorm options
});
@Entity()
class User {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
email: string;
}
export class UserFactory extends TypeormFactory<User> {
entity = User;
attrs = {
email: '[email protected]',
};
}
// Usage
const userFactory = new UserFactory(dataSource);
const user = userFactory.create();
const users = userFactory.createMany(5);
对象和接口
您可以直接使用没有定义的实体的工厂, 并且只提供接口 .
import { ObjectFactory } from '@adrien-may/factory';
interface User {
name: string;
}
class UserFactory extends ObjectFactory<User> {
// No entity defined
attrs = {
name: 'John Doe',
};
}
// Usage
const userFactory = new UserFactory();
const user = userFactory.create();
const users = userFactory.createMany(5);
自定义
你可能想用工厂 和其他ORM或CMS。 为了说明这一点,我们将展示 你将如何建立一个工厂 有效载荷cms
为此,您应该首先创建一个适配器.
import { Adapter } from '@teammay/factory'
import {
DataFromCollectionSlug,
RequiredDataFromCollectionSlug,
} from 'node_modules/payload/dist/collections/config/types'
interface PayloadArgs {
draft: boolean;
locale: string
}
class PayloadAdapter extends Adapter<S extends CollectionSlug, [PayloadArgs?]> {
constructor(
private payload: Payload,
private slug: S,
) {}
async save(
instance: RequiredDataFromCollectionSlug<S> | RequiredDataFromCollectionSlug<S>[],
{ draft = false, locale = 'en' }: PayloadArgs = { draft: false, locale: 'en' },
): Promise<DataFromCollectionSlug<S> | DataFromCollectionSlug<S>[]> {
const instances = Array.isArray(instance) ? instance : [instance]
const savedInstances = await Promise.all(
instances.map((data) =>
this.payload.create({
collection: this.slug,
data,
draft,
locale,
}),
),
)
return Array.isArray(instance) ? savedInstances : savedInstances[0]
}
}
现在有了这个适配器,我们就能创造出一个"有效载荷"
import { Factory } from '@teammay/factory'
import {
DataFromCollectionSlug,
RequiredDataFromCollectionSlug,
} from 'node_modules/payload/dist/collections/config/types'
import { CollectionSlug, Payload } from 'payload'
abstract class PayloadFactory<S extends CollectionSlug> extends Factory<DataFromCollectionSlug<S>, [PayloadArgs?]> {
constructor(protected payload: Payload, protected slug: S) {
super()
this.adapter = new PayloadAdapter(payload, slug)
}
}
有了这门课,我们现在可以 即兴制造工厂的每一个有效载荷收集。 例如, 如果我们有一个带有 swit'author' 的集合, 并且只包含一个字段“ 名字 ” , 我们就会这样做 :
interface User {
name: string;
}
class UserFactory extends PayloadFactory<'author'> {
// No entity defined
attrs = {
name: 'John Doe',
};
}
// Usage
const userFactory = new UserFactory();
const user = userFactory.create({}, {draft: true, locale: 'fr'});
const users = userFactory.createMany(5, {}, {draft: false, locale: 'es'});
带有Fakerjs/Chancejs/的模糊生成
为了为工厂生成伪随机数据,我们可以利用图书馆:
import { TypeormFactory } from '@adrien-may/factory';
import Chance from 'chance';
const chance = new Chance();
export class ProfileFactory extends TypeormFactory<Profile> {
entity = Profile;
attrs = {
name: () => chance.name(),
email: () => chance.email(),
description: () => chance.paragraph({ sentences: 5 }),
};
}
注:Faker/Change不在本库. 我们只利用一个函数传递给 attrs 每次创建工厂都叫它 因此,您可以使用Faker/Chancejs生成数据.
利用我们创造的工厂
我们可以利用我们的工厂创造新的实体实例:
const userFactory = new UserFactory();
对于打字机工厂,您应当使用下列方法设置默认工厂:
import { setDefaultFactory } from '@adrien-may/factory';
{...}
setDefaultDataSource(typeormDatasource)
{...}
const userFactory = new UserFactory();
或者为每个实例设置数据源,使用:
const userFactory = new UserFactory(typeormDatasource);
工厂及其适配器暴露出一些功能:
- 构建:创建对象(并最终生成数据/子事实)
- 创建: 通过 ORM "保存" 方法在数据库中与 make 相同但持久性对象
- 建立 Many 和 Create Many 允许在一次中创建多个实例
const user: User = await userFactory.create();
const users: User[] = await userFactory.createMany(5);
要覆盖工厂默认属性, 请将它们添加为创建函数的参数 :
const user: User = await userFactory.create({ email: '[email protected]' });
子事实
实体之间(许多ToOne,一个ToOny,一个TOOne等)有关系是很常见的. 在这种情况下,我们为所有实体创建工厂,并利用 SubFactory 在他们之间建立联系。 子事实将在建立实例时解决 。 注意,这是纯粹的合成糖,因为人们可以使用一个叫另一个工厂的箭头函数.
示例
如果一个用户有一个配置实体与之链接:我们使用 UserFactory 作为子事实 ProfileFactory
import { Factory, SubFactory } from '@adrien-may/factory';
import { UseFactory } from './user.factory.ts'; // factory naming is free of convention here, don't worry about it.
export class ProfileFactory extends Factory<Profile> {
entity = Profile;
attrs = {
name: 'Handsome name',
user: new SubFactory(UserFactory, { name: 'Override factory name' }),
};
}
顺序
序列允许你得到一个递增值 每次运行在同一工厂. 这样,你可以使用一个计数器来获得更有意义的数据.
import { Factory, Sequence } from '@adrien-may/factory';
export class UserFactory extends Factory<User> {
entity = User;
attrs = {
customerId: new Sequence((nb: number) => `cust__abc__xyz__00${nb}`),
};
}
懒惰属性
懒惰属性在您想要根据正在创建的实例生成值时有用 . 它们按其他属性解决,但在拯救实体之前解决。 对于任何动作"后保存",使用后Generate钩.
import { Factory, LazyAttribute } from '@adrien-may/factory';
export class UserFactory extends Factory<User> {
entity = User;
attrs = {
name: 'Sarah Connor',
mail: new LazyAttribute(instance => `${instance.name.toLowerCase()}@skynet.org`),
};
}
懒惰序列
懒惰的序列结合了序列和懒惰属性的力量. 调用时使用递增数并创建实例.
import { Factory, LazySequence } from '@adrien-may/factory';
export class UserFactory extends Factory<User> {
entity = User;
attrs = {
name: 'Sarah Connor',
mail: new LazySequence((nb, instance) => `${instance.name.toLowerCase()}.${nb}@skynet.org`),
};
}
后世代数
要在创建实例后执行动作, 您可以使用 PostGeneration 装饰师.
import { Factory, PostGeneration } from '@adrien-may/factory';
export class UserFactory extends Factory<User> {
...
@PostGeneration()
postGenerationFunction() {
// perform an action after creation
}
@PostGeneration()
async actionOnCreatedUser(user: User) {
// do something with user
}
}
适配器
您可以提供自己的适配器来扩展此库. 这个图书馆现在提供 ObjectAdapter (违约)和 TypeormAdapter 。 。 。 .
为方便测试,此库提供 TypeormFactory 课.
以下例子说明如何使用这些工具:
import { Factory, TypeormFactory, TypeormAdapter } from '@adrien-may/factory';
export class UserFactory extends Factory<User> {
entity = User;
attrs = {
email: '[email protected]',
role: 'basic',
};
adapter = TypeormAdapter();
}
// Same as:
export class TypeormUserFactory extends TypeormFactory<User> {
entity = User;
attrs = {
email: '[email protected]',
role: 'admin',
};
}
种子( NEW)
你现在可以很容易地在你的数据库里 使用你的工厂 runSeed 函数.
示例
创建一个 seed.ts 在工程中的文件 :
import { runSeed } from '@adrien-may/factory';
import { UserFactory } from './factories/user.factory';
import { PostFactory } from './factories/post.factory';
import { dataSource } from './db'; // your TypeORM DataSource
export let fixtures: any = {};
async function main() {
fixtures = await runSeed({
dataSource,
factories: { UserFactory, PostFactory },
seeds: async ({ UserFactory, PostFactory }) => {
const users = await UserFactory.createMany(10);
const posts = await PostFactory.createMany(20);
return {
users,
posts,
};
},
});
}
main();
- 所有工厂都与提供
dataSource。 。 。 . - 您可以使用所有厂房方法( 创建、 创建 Many等)
seeds函数. - 这种功能具有框架不可知性,但与TypeORM最有效.
- 您可以通过
fixtures对象.
示例: 在 Jest 设置中种子
您还可以使用 runSeed 在您的 Jest 设置中确保测试数据库在测试运行前被种子 :
// jest.setup.ts
import { runSeed } from '@adrien-may/factory';
import { UserFactory } from './factories/user.factory';
import { dataSource } from './db';
beforeAll(async () => {
await runSeed({
dataSource,
factories: { UserFactory },
seeds: async ({ UserFactory }) => {
await UserFactory.createMany(5);
},
});
});
示例:通过 npm 运行种子
将此添加到您的 package.json 脚本 :
"scripts": {
"seed": "ts-node ./seed.ts"
}
然后运行 :
npm run seed