Adonis.js - Advanced factories
Published 2/10/2020
For a while now I have been using these little tricks to simplify writing factories.
Nr. 1: override
Oftentimes you write a factory that requires a foreign key from another table like here:
Factory.blueprint('App/Models/Subscription', async (faker, i, data) => {
return {
user_id: await Factory.model('App/Models/User').create().then(user => user.id),
plan: 'monthly',
}
})
But sometimes you already have a user and don't want to create a new one, so you need to add some logic to your blueprint
Factory.blueprint('App/Models/Subscription', async (faker, i, data = {}) => {
return {
user_id: data.user_id || await Factory.model('App/Models/User').create().then(user => user.id),
plan: 'monthly',
}
})
and then call it like this
Factory.model('App/Models/Subscription', { user_id: 1 })
Having to do this repeatedly can get quite cumbersome, because you have to write a lot of it for your tests. I have created this little "magic" method that automates this for you: https://github.com/MZanggl/adonis-override-factory.
Our blueprint from before now becomes
Factory.blueprint('App/Models/Subscription', async (faker, i, data) => {
return override({
user_id: () => Factory.model('App/Models/User').create(),
plan: 'monthly',
}, data)
})
Note how the default data is now wrapped inside the "override" method. The "Factory...create()" is also wrapped in a higher-order function to avoid executing it when data was passed.
Finally, there is no need for .then(user => user.id)
because the "override" method resolves the id automatically when it receives an object.
Check out my e-book!
Nr 2: 'Factory.model' => 'factory'
Inside vowfile.js
where you set up the test environment I have added this little global helper:
const Factory = use('Factory')
global.factory = (model) => {
return Factory.model(model)
}
So instead of writing
const Factory = use('Factory')
Factory.model('App/Models/User').create()
we can now do
factory('App/Models/User').create()
It's again a way to save some keystrokes. While globals are considered harmful, there are some use cases and there is no problem to use globals when the price is right.
I like to make not too many modifications to the framework as it can make upgrading more difficult and the project becomes more custom in general, so I try to keep such things to a minimum and only apply them after a long trial period with the things the framework provides by default.