class UserRegistration {
async sendVerificationEmail (user) {
await Mail.send('emails.verify', user, (message) => {
message.to(user.email)
message.subject('Verify account')
})
}
}
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
Quite often, you’ll want to swap out the original implementation of certain parts of your application with a fake implementation when writing tests.
Since AdonisJs leverages an IoC container to manage dependencies, it’s incredibly easy to create fake implementations when writing tests.
Let’s start with a basic example of faking a service that normally sends emails.
Creating too many test fakes may lead to false tests, where all you are testing is the syntax and not the implementation. As a rule, try to keep fakes as the last option when writing your tests. |
class UserRegistration {
async sendVerificationEmail (user) {
await Mail.send('emails.verify', user, (message) => {
message.to(user.email)
message.subject('Verify account')
})
}
}
Without implementing a fake, each time we test our application’s user registration logic, a verification email would be sent to the passed email address!
To avoid this behavior, it makes sense to fake the UserRegistration
service:
const { ioc } = use('@adonisjs/fold')
const { test } = use('Test/Suite')('User registration')
test('register user', async () => {
ioc.fake('App/Services/UserRegistration', () => {
return {
sendVerificationEmail () {}
}
})
// code to test user registration
// ....
ioc.restore('App/Services/UserRegistration')
})
The ioc.fake
method lets you bind a fake value to the IoC container, and when any part of the application tries to resolve the bound namespace, the fake value is returned instead of the actual value.
Once finished with a fake, ioc.restore
can be called to remove it.
This approach works great for a majority of use cases until you can create a fake which is similar to the actual implementation. For greater control, you can use external libraries like Sinon.JS.
The AdonisJs Mail Provider comes with a built-in fake
method:
const Mail = use('Mail')
const { test } = use('Test/Suite')('User registration')
test('register user', async ({ assert }) => {
Mail.fake()
// write your test
const recentEmail = Mail.pullRecent()
assert.equal(recentEmail.message.to[0].address, 'joe@example.com')
assert.equal(recentEmail.message.to[0].name, 'Joe')
Mail.restore()
})
Calling Mail.fake
binds a fake mailer to the IoC container. Once faked, all emails are stored in memory as an array of objects which can be later run assertions against.
The following methods are available on the fake mailer.
Returns the recent email object and removes it from the in-memory array:
Mail.pullRecent()
The AdonisJs Event Provider also comes with a built-in fake
method:
const Event = use('Event')
const { test } = use('Test/Suite')('User registration')
test('register user', async ({ assert }) => {
Event.fake()
// write your test
....
const recentEvent = Event.pullRecent()
assert.equal(recentEvent.event, 'register:user')
Event.restore()
})
Calling Event.fake
binds a fake event emitter to the IoC container. Once faked, all emitted events are stored in memory as an array of objects which can be later run assertions against.
You can also trap
an event inline and run assertions inside the passed callback:
test('register user', async ({ assert }) => {
assert.plan(2)
Event.fake()
Event.trap('register:user', function (data) {
assert.equal(data.username, 'joe')
assert.equal(data.email, 'joe@example.com')
})
// write your test
....
Event.restore()
})
The following methods are available on the fake event emitter.
Returns the recent event object and removes it from the in-memory array:
Event.pullRecent()
Keeping your database clean for each test can be difficult.
AdonisJs ships with a DatabaseTransactions
trait that wraps your databases queries inside a transaction then rolls them back after each test:
const { test, trait } = use('Test/Suite')('User registration')
trait('DatabaseTransactions')
Alternatively, you could set a Lifecycle Hook to truncate your database tables after each test, but using the DatabaseTransactions
trait would be far simpler.