DEV Community

Michael Z
Michael Z

Posted on • Edited on • Originally published at michaelzanggl.com

3 2

Adonis.js - Advanced factories

Originally posted at michaelzanggl.com. Subscribe to my newsletter to never miss out on new content.

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',
  }
})
Enter fullscreen mode Exit fullscreen mode

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',
  }
})
Enter fullscreen mode Exit fullscreen mode

and then call it like this

Factory.model('App/Models/Subscription', { user_id: 1 })
Enter fullscreen mode Exit fullscreen mode

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)
})
Enter fullscreen mode Exit fullscreen mode

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.

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)
}
Enter fullscreen mode Exit fullscreen mode

So instead of writing

const Factory = use('Factory')
Factory.model('App/Models/User').create()
Enter fullscreen mode Exit fullscreen mode

we can now do

factory('App/Models/User').create()
Enter fullscreen mode Exit fullscreen mode

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.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up