typescriptooptestingfactory

Dynamic values for objects built by factories


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.


Solution

  • 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
    }