thoughtbot / fishery

A library for setting up JavaScript objects as test data
MIT License
877 stars 36 forks source link

Extending factories receiving params and associations #39

Closed Sabrinovsky closed 3 years ago

Sabrinovsky commented 4 years ago

Hi, i did not found a example to do something like this on the doc. I want to create some kind of factory with trait but without writing class.

Is this aproach ok?

const userFactory = Factory.define(({ sequence }) => ({
  id: sequence
}));

const baseFactory = notificationFactory.build()

const adminFactory = Factory.define(({associations})=>({
  ...baseFactory,
  admin: true,
  associatedTo: userFactory.build() || associations.associatedTo
}))
stevehanson commented 3 years ago

@Sabrinovsky apologies for the delay in getting back to you. It's a little bit unclear to me what notificationFactory is doing in this example, so I can't say if it is the best approach or not for your case, but there is nothing that inherently wouldn't work with your example above. As long as factory.define returns the expected data type, you are free to do whatever you need to to generate the data.

I'm not sure if this is relevant or not to your example, but, generally, if you are trying to avoid writing classes and have multiple factories that return the same data type, I would recommend using the factory extension methods to build on to other factories:

const userFactory = Factory.define(({ sequence }) => ({
  id: sequence,
  name: 'Jan'
}));

const adminFactory = userFactory.params({
  admin: true,
})

const adminUser = adminFactory.build()
adminUser.admin // true
adminUser.name // Jan

I understand you might have moved on by now, but let me know if you have more questions!

sPaCeMoNk3yIam commented 3 years ago

I feel a huge downside to the extensions is that you don't have access to the arguments in define. So you end up only being able to add static fields. This is also true for within extension classes, so I ended up in using the this._associations e.g. which doesn't feel right. Considering this, the initial solution by @Sabrinovsky is actually a good one, if you would ...notificationFactory.build(arguments):

const adminFactory = Factory.define((arguments)=>({
  ...notificationFactory.build(arguments),
  admin: true,
  associatedTo: userFactory.build() || arguments.associations.associatedTo
}))

Downside, again, is sequence would make problems since within notificationFactory#build another sequence would be generated as in adminFactory#build.

stevehanson commented 3 years ago

Closing since I haven't heard back from the original poster.

@sPaCeMoNk3yIam thanks for weighing in. I have noted your use-case of wanting access to other arguments of define in the extension methods. It's something I've thought about a bit as well and will consider in the future.