Testing made easy with AdonisJs
Tags: node, testing, tdd
Published: 2019-08-18 13:18:21

> Originally posted at [michaelzanggl.com](https://michaelzanggl.com/articles/testing-made-easy-with-adonisjs). Subscribe to [my newsletter](https://michaelzanggl.com/) to never miss out on new content.

Adonis lets you write really clean tests and is a good candidate for test driven development. I will show how to get started with testing as well as some common techniques regarding the database setup. You can read up about more things in the official docs https://adonisjs.com/docs/4.1/.

## Setup

```bash
adonis install @adonisjs/vow
```

Next, add vowProvider under `start/app.js` in the `aceProviders` array.

```javascript
const aceProviders = [
    '@adonisjs/vow/providers/VowProvider',
]
```

Finally, run `adonis test` to run all tests.

## Dealing with Database

Installing vow creates the two files `vowfile.js` and `.env.testing`, as well as an example test.

### Test database
Copy over the database settings from your `.env` to `.env.testing`. Make sure to change `DB_DATABASE` to a different database for testing. To make the tests run faster, pick sqlite as the `DB_CONNECTION` (You need to install the npm dependency sqlite3 for it).

### Migrations

If you use migrations, you can easily fill your new testing database before running tests and reset it again after running them.

Simply go inside the file `vowfile.js` and uncomment all the lines needed for migrations. In essence, this is what the file looks like

```javascript
'use strict'

const ace = require('@adonisjs/ace')

module.exports = (cli, runner) => {
  runner.before(async () => {
    use('Adonis/Src/Server').listen(process.env.HOST, process.env.PORT)

    await ace.call('migration:run', {}, { silent: true })
  })

  runner.after(async () => {
    use('Adonis/Src/Server').getInstance().close()

    await ace.call('migration:reset', {}, { silent: true })
  })
}

```

### Resetting transactions after each test

You don't want a test to accidentally depend on data inserted by a different test. To keep tests simple, it's best to rollback all inserted data after each test. We can do this by using the DatabaseTransactions trait. All queries get wrapped in a transaction that gets rolled back automatically. 

Here is an example:

```javascript
'use strict'

const { test, trait } = use('Test/Suite')('suite name')

trait('DatabaseTransactions')

test('name of test', async ({ assert }) => { })
```

### Model factories

We often rely on data in the database for our tests. It would be quite painful though to always insert the data manually. To make life simple, we can create [model factories](https://adonisjs.com/docs/4.1/seeds-and-factories#_model_factory_api) inside `database/factory.js`. Here is an example for a user factory:

```javascript
const Factory = use('Factory')

Factory.blueprint('App/Models/User', (faker, i, data) => {
  return {
    username: faker.username(),
    email: faker.email(),
    password: 'test-password',
    ...data,
  }
})
```

Inside the tests, you can now create users easily
```javascript
const Factory = use('Factory')

Factory.model('App/Models/User').create()
```

We can also override factory data.

```javascript
const Factory = use('Factory')

Factory.model('App/Models/User').create({ email: 'email@test.com' })
```

## Examples

### Browser test & Faking emails

```javascript
'use strict'

const { test, trait } = use('Test/Suite')('ForgotPasswordController')
const Factory = use('Factory')
const Mail = use('Mail')

trait('Test/ApiClient')
trait('DatabaseTransactions')

test('sends forgot password email to user', async ({ assert, client }) => {
  Mail.fake()
  const user = await Factory.model('App/Models/User').create()

  await client.post('/password/forgot').send({ email: user.email }).end()
  const mail = Mail.pullRecent()
  assert.equal(mail.message.to[0].address, user.email)
  assert.equal(mail.message.subject, 'Password Reset')

  Mail.restore()
})
```

### Checking controller response

```javascript
test('resets password with correct token', async ({ assert, client }) => {
  const user = await Factory.model('App/Models/User').create()
  const token = await (new TokenService).generateToken(user.email)

  const response = await client.post('/password/reset').send({ email: user.email, token, password: 'new password' }).end()
  await user.reload()

  response.assertStatus(200)
  response.assertJSON({ message: 'Password reset successful.' })
  assert.isTrue(await user.verify('new password'))
})
```

---

This should give you a good idea on how to get started with testing with the adonis framework. Make sure to read the official docs for more information.

> Read more about faking mails, events and dependencies in an ioc container here: https://adonisjs.com/docs/4.1/testing-fakes

> Read more about http tests, different request types, authentication, sessions and assertion types here: https://adonisjs.com/docs/4.1/api-tests

Have fun testing and TDDing!

---

If this article helped you, I have a lot more tips on simplifying writing software [here](https://michaelzanggl.gumroad.com/l/intent-driven-development).