Run test with ENV_NODE=testing


#1

Hello,

I’m pretty new to testing and I need some advices to setup my environment.

First, I need to perform migration:runand migration:resetto clean the database after each test. That’s what I do in after() and before() hook

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

before(async () => {
  await ace.call('migration:run')
})

after(async () => {
  await ace.call('migration:reset')
})

Then I test my API by using ApiClient trait :

trait('Test/ApiClient')

test('Register new user', async ({ client }) => {

  const response = await client
    .post('/user/register')
    .send({ ... })
    .end()

  response.assertStatus(201)
  response.assertJSONSubset({
    status: 'success',
    data: { ... }
  })   


})

I don’t want to pollute my development database, so I created .env.testing file to override the database name and set it to test database.
My problem is that the apiClient will send request to the server which runs under NODE_ENV=development, and therefore requests will be executed on my development database. I excepted adonis test to take in account my .env.testing entirely but I’m probably wrong
However, migrations ran within tests are well executed on my test datatbase

Is the only solution is running manually a second instance of my app with NODE_ENV=testing ? and change port if I want them both running together ?

Thanks for you help


#2

Hey ! :wave:

You can simply use the DatabaseTransactions trait in your test.

Example: https://github.com/adonisjs/adonis-blog-demo/blob/master/test/functional/register.spec.js#L8


#3

why don’t you just initiate your db credential in env.testing?
examle :

HOST=127.0.0.1
PORT=4000
NODE_ENV=testing
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=test_user
DB_PASSWORD=test_password
DB_DATABASE=test_db

#4

I’ve solved this by editing the vowfile.js at the project root. The below configuration will automatically create and migrate the test database before all of your tests run:

vowfile.js

const { isString, isObject } = require('lodash')

const Config = use('Config')
const Server = use('Adonis/Src/Server')

const defaultConf = Config.get(`database.${Config.get('database.connection')}`)
const { connection: dbConnection } = defaultConf
const datname = isString(dbConnection) 
  ? new URL(dbConnection).pathname.split('/')[1] 
  : isObject(dbConnection) ? dbConnection.database : undefined

const defaultDatabase = connection => {
  let newConnection

  if (isString(connection)) {
    const connUrl = new URL(connection)
    connUrl.pathname = defaultConf.client == 'pg' ? 'postgres' : ''
    newConnection = connUrl.href
  } else if (isObject(connection)) {
    newConnection = Object.assign({}, connection)

    if (defaultConf.client === 'pg') {
      newConnection.database = 'postgres'
    } else {
      delete newConnection.database
    }
  } else {
    throw new TypeError('Connection argument must be a string or an object.');
  }

  return newConnection;
}

// Vanilla connection without the database name
const conf = Object.assign({}, defaultConf)
conf.connection = defaultDatabase(dbConnection)

module.exports = (cli, runner) => {
  runner.before(async () => {
    /*
    |--------------------------------------------------------------------------
    | Start the server
    |--------------------------------------------------------------------------
    |
    | Starts the http server before running the tests. You can comment this
    | line, if http server is not required
    |
    */
    Server.listen(process.env.HOST, process.env.PORT)

    /*
    |--------------------------------------------------------------------------
    | Create test database
    |--------------------------------------------------------------------------
    |
    | Create the test database before starting the tests.
    |
    */
    if (!datname) {
      throw new Error('Invalid database configuration')
    }

    let knex = require('knex')(defaultConf)

    try {
      await knex('pg_database')
    } catch (e) {
      if (/database \"(.*)\" does not exist/i.test(e.message)) {
        // Connect without selecting database.
        knex = require('knex')(conf)
        await knex.raw(`CREATE DATABASE ${datname};`)
      }
    } finally {
      await knex.destroy()
    }

    /*
    |--------------------------------------------------------------------------
    | Run migrations
    |--------------------------------------------------------------------------
    |
    | Migrate the database before starting the tests.
    |
    */
    await ace.call('migration:run')

    /*
    |--------------------------------------------------------------------------
    | Seed data
    |--------------------------------------------------------------------------
    |
    */
    await ace.call('seed')
  })

  runner.after(async () => {
    /*
    |--------------------------------------------------------------------------
    | Shutdown server
    |--------------------------------------------------------------------------
    |
    | Shutdown the HTTP server when all tests have been executed.
    |
    */
    Server.getInstance().close()

    /*
    |--------------------------------------------------------------------------
    | Rollback migrations
    |--------------------------------------------------------------------------
    |
    | Once all tests have been completed, we should reset the database to it's
    | original state
    |
    */
    await ace.call('migration:reset')

    /*
    |--------------------------------------------------------------------------
    | Teardown database
    |--------------------------------------------------------------------------
    |
    */
    const knex = require('knex')(conf)
    await knex.raw(`DROP DATABASE ${datname};`)
    await knex.destroy()
  })
}

I actually have this moved into a separate ace provider for cleaner code but for the purpose of demonstration, I weaved them into the vowfile.js directly.

Once you run node ace test, you should see and output like this:

With the above configuration, the Server should run with testing environment so that it uses the test database configured in .env.testing. It will also create a new test database and drop the db after the tests are run so that each test run starts off in a clean state.

Hope that helps.


#5

You can use the .env.testing file to configure this.