I have a factory for building objects when testing. Below is the code. As you can see, I have one private object, which is the object I will use to build the object the factory returns; I also have methods for building one and many objects: they simply instantiate one object with the the build_obj
; the other methods allow me to customize the object I want returned: if I want it closed or not, within a specific range of dates, etc.
export class BudgetFactory {
private build_obj: IBudgetData = {
id: faker.number.int(),
title: faker.lorem.sentence(3),
description: faker.lorem.paragraph(4),
opening_date: faker.date.future(),
closing_date: faker.date.future(),
}
public getOne(): Budget {
return new Budget(this.build_obj)
}
public getMany(quantity?: number): Array<Budget> {
let items = []
for (let i = 0; i < quantity; i++) {
items.push(this.getOne())
}
return items
}
public isOnPeriod(opening_date?: Date, closing_date?: Date): this {
this.build_obj.opening_date = opening_date || faker.date.past()
this.build_obj.closing_date = closing_date || faker.date.future()
return this
}
public periodIsClosed(opening_date?: Date, closing_date?: Date): this {
this.build_obj.closing_date = closing_date || faker.date.past()
this.build_obj.opening_date =
opening_date || new Date(this.build_obj.closing_date.getTime() - 7 * 24 * 60 * 60 * 1000)
return this
}
public periodIsNotOpenYet(opening_date?: Date, closing_date?: Date): this {
this.build_obj.opening_date = opening_date || faker.date.future()
this.build_obj.closing_date =
closing_date || new Date(this.build_obj.opening_date.getTime() - 7 * 24 * 60 * 60 * 1000)
return this
}
public isClosed(date?: Date): this {
this.build_obj.closing_date = date || faker.date.past()
return this
}
}
There's just one problem regarding the getMany
method: the array of objects it will return has the same values of build_obj
for every object, because it's a property. I need a way to build the object, with the customizing methods, but every object must have its own values.
Solved it myself. What I did was register in a private variable every option method that was called with a decorator, and then, when calling the getMany
I recalled every method with a reduce
, so the values would be different (but would still be the option I wanted).
Here how I recalled the option methos together with the getMany
:
public getMany(quantity: number): Array<Return> {
let items = []
for (let i = 0; i < quantity; i++) {
items.push(this.recallOptionMethods())
}
return items
}
private recallOptionMethods(): Return {
return this.called_options
.reduce((instance, method) => instance[method](), new this.subclass())
.getOne()
}
And here is my decorator and how I used it to store every option method that was called (this is for generating multiple objects with the same rules, but different values).
Decorator usage inside BudgetFactory
:
@registerFactoryOptions
public isOnPeriod(opening_date?: Date, closing_date?: Date): this {
this.build_obj.opening_date = opening_date || faker.date.past()
this.build_obj.closing_date = closing_date || faker.date.future()
return this
}
@registerFactoryOptions
public periodIsClosed(opening_date?: Date, closing_date?: Date): this {
this.build_obj.closing_date = closing_date || faker.date.past()
this.build_obj.opening_date =
opening_date || new Date(this.build_obj.closing_date.getTime() - 7 * 24 * 60 * 60 * 1000)
return this
}
@registerFactoryOptions
public periodIsNotOpenYet(opening_date?: Date, closing_date?: Date): this {
this.build_obj.opening_date = opening_date || faker.date.future()
this.build_obj.closing_date =
closing_date || new Date(this.build_obj.opening_date.getTime() + 7 * 24 * 60 * 60 * 1000)
return this
}
And here is the decorator:
export function registerFactoryOptions(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value
descriptor.value = function (...args: any[]) {
this.called_options.push(key)
return originalMethod.apply(this, args)
}
return descriptor
}