Mocking and Fakes
AdonisJS ships with fake implementations for most of its first-party packages. You can use fakes to have a better testing experience without manually mocking parts of your codebase.
You can fake the outgoing emails by calling the Mail.fake
method. Once you call this method, all other parts of your application interacting with the Mail
object will not send real emails. Instead, they will be collected within memory for assertions.
import { test } from '@japa/runner'
import Mail from '@ioc:Adonis/Addons/Mail'
test('register user', async ({ assert, client }) => {
const mailer = Mail.fake()
await client
.post('register')
.form({
email: 'virk@adonisjs.com',
password: 'secret'
})
// Time for assertions
assert.isTrue(mailer.exists((mail) => {
return mail.subject === 'Welcome to AdonisJS'
}))
Mail.restore()
})
Mail.fake
Calling Mail.fake
creates a fake only for the default mailer. However, you can explicitly pass the name(s) of the mailers to fake.
// Fake the default mailer
Mail.fake()
// Fake smtp and s3 mailers
Mail.fake(['smtp', 's3'])
Mail.restore
Once done with testing, you can restore the fakes for selected or all mailers.
// Restore the default mailer
Mail.restore()
// Restore smtp and s3 mailers
Mail.restore(['smtp', 's3'])
// Restore all faked mailers
Mail.restoreAll()
Finding messages
You can check for the sent messages using exists
, find
, or the filter
methods. All the methods accepts a subset of message properties or a callback.
assert.isTrue(mailer.exists({ subject: 'Welcome to AdonisJS' }))
assert.isTrue(mailer.exists((mail) => {
return mail.subject === 'Welcome to AdonisJS'
}))
const message = mailer.find((mail) => {
return mail.to[0].address === 'virk@adonisjs.com'
})
console.log(message)
Events
You can fake events by calling the Event.fake
method. The method accepts an optional array of events to fake. Otherwise, all upcoming events are faked.
import Event from '@ioc:Adonis/Core/Event'
// Fake all events
Event.fake()
// Fake specific events
Event.fake(['new:user', 'update:email'])
Event.restore
You can restore events using the Event.restore
method.
Event.restore()
Finding events
The Event.fake
method returns a fake emitter you can use to later fetch or find events.
const emitter = Event.fake()
assert.isTrue(emitter.exists('new:user'))
assert.isTrue(emitter.exists((event) => {
return event.name === 'new:user' && event.data.id === 1
}))
You can use the find
and filter
methods to find specific events.
const emitter = Event.fake()
emitter.find('new:user')
// returns { name: 'new:user', data: any }
emitter.filter((event) => event.name.startsWith('invite:'))
// returns { name: 'new:user', data: any }
Drive
You can fake the Drive implementation by calling the Drive.fake
method. By default, only the default disk is faked. However, you can define disk names explicitly as well.
import Drive from '@ioc:Adonis/Core/Drive'
// Fake default disk
Drive.fake()
// Fake local and s3
Drive.fake(['s3', 'local'])
Drive.restore
You can restore fakes by calling the Drive.restore
method. Optionally, you can pass the disk names to restore, otherwise the default disk is restored. Or, use the Drive.restoreAll
method to restore all the disk.
// Restore default disk
Drive.restore()
// Restore specific disks
Drive.restore(['s3', 'local'])
// Restore all the disks
Drive.restoreAll()
Finding files
The Drive.fake
method returns the fake drive object you can use to later fetch or find files.
const drive = Drive.fake()
// Find if file exists
assert.isTrue(await drive.exists('avatar.jpg'))
// Assert for the file size
assert.isBelow(await drive.bytes('avatar.jpg'), 1000 * 1000 * 20)
// Assert for file contents
assert.equal(await drive.get('package.json'), JSON.stringify({}))
Hashing
You can fake the Hash module by calling the Hash.fake
method. No password hashing is performed during the fake, and the hash.make
method returns the same value.
import Hash from '@ioc:Adonis/Core/Hash'
// Fake hash implementation
Hash.fake()
const hashed = await Hash.make('secret') // returns "secret"
await Hash.verify(hashed, 'secret') // returns "true"
// Restore fake
Hash.restore()
Mocking objects
AdonisJS does not ship with any mocking library out of the box. You are free to use any mocking library from the Node ecosystem.
Following is a small example demonstrating the usage of SinonJS to mock ES6 classes.
export default class ExchangeService {
constructor (private baseCurrency: string) {}
public getRate(currency: string, amount: number) {
}
}
During tests, you can import the ExchangeService
and mock the getRate
method as follows.
import { test } from '@japa/runner'
import sinon from 'sinon'
import ExchangeService from 'App/Services/ExchangeService'
test('transfer payment', async ({ client }) => {
const mock = sinon.mock(ExchangeService.prototype)
mock
.expects('getRate')
.once()
.withArgs('INR', 600)
.returns(6)
await client
.post('/transfer')
.form({ currency: 'INR', amount: 600 })
mock.verify()
mock.restore()
})
Mocking network requests
You can use nock
to mock the outgoing network requests. Since nock works by overriding the Node.js http.request
, it works with almost every HTTP client, including axios
and got
.
Following is an example to mock the charges
API of Stripe.
import nock from 'nock'
export function mockStripeCharge() {
return nock('https://api.stripe.com/v1')
.post('/charges')
.reply(201, (_, requestBody) => {
return {
id: 'ch_3KjEE62eZvKYlo2C0n3A7N3E',
object: 'charge',
amount: requestBody.amount,
}
})
}
Now, you can use the mockStripeCharge
helper as follows.
import { mockStripeCharge } from 'TestHelpers/mocks'
test('complete purchase with stripe charge', async () => {
mockStripeCharge()
// Make a call to stripe API here
})