Dynamic values for objects built by factories

29 Views Asked by At

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.

1

There are 1 best solutions below

0
Bernardo Benini Fantin On BEST ANSWER

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
}