Introduction

Testing

AdonisJS has in-built support for writing tests. You do not have to install additional packages or wire up your application to be ready for testing - All the hard work has already been done.

You can run the application tests using the following ace command.

node ace test

The tests are stored inside the tests directory and we further organize tests by their type. For example, the functional tests are stored inside the tests/functional directory, and the unit tests are stored inside the tests/unit directory.

Functional tests refer to outside-in testing in which you will make real HTTP requests to your application to test the functionality of a given flow or an endpoint. For example, you may have a collection of functional tests for creating a user.

Some communities might refer to functional tests as feature tests or end-to-end tests. AdonisJS is flexible about what you call them. We decided to settle on the term functional tests.

Configuring the tests runner

AdonisJS uses Japa for writing and running tests. Therefore, we recommend reading the Japa documentation to understand its APIs and configuration options better.

Suites

The test suites are defined inside the adonisrc.ts file. By default, we register the functional and the unit test suites. If needed, you can remove the existing suites and start from scratch.

{
tests: {
suites: [
{
name: 'functional',
files: ['tests/functional/**/*.spec.(js|ts)']
},
{
name: 'unit',
files: ['tests/unit/**/*.spec.(js|ts)']
}
]
}
}
  • A suite combines the suite's unique name and the file's glob pattern.
  • When you run tests for a specific suite, files only related to that suite are imported.

You can configure a suite at runtime using the configureSuite hook defined inside the tests/bootstrap.ts file. For example, when running functional tests, you can register suite-level hooks to start the HTTP server.

export const configureSuite: Config['configureSuite'] = (suite) => {
if (['browser', 'functional', 'e2e'].includes(suite.name)) {
return suite.setup(() => testUtils.httpServer().start())
}
}

Runner hooks

Runner hooks are global actions you can run before and after all the tests. The hooks are defined using the runnerHooks property inside the tests/boostrap.ts file.

export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
setup: [
() => {
console.log('running before all the tests')
}
],
teardown: [
() => {
console.log('running after all the tests')
}
],
}

Plugins

Japa has a plugin system you can use to extend its functionality. Plugins are registered inside the tests/bootstrap.ts file.

See also: Creating Japa plugins

export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app)
]

Reporters

Reporters are used for reporting/displaying the progress of tests as they run. The reporters are registered inside the tests/bootstrap.ts file.

See also: Creating Japa reporters

export const reporters: Config['reporters'] = {
activated: ['spec']
}

Creating tests

You may create a new test using the make:test command. The command needs the suite's name to create the test file.

See also: Make test command

node ace make:test posts/create --suite=functional

The file will be created inside the directory configured using the files glob property.

Writing tests

The tests are defined using the test method imported from the @japa/runner package. A test accepts a title as the first parameter and the implementation callback as the second parameter.

In the following example, we create a new user account and use the assert object to ensure the password hashed correctly.

import { test } from '@japa/runner'
import User from '#models/User'
import hash from '@adonisjs/core/services/hash'
test('hashes user password when creating a new user', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})

Using test groups

Test groups are created using the test.group method. Groups add structure to your tests and allow you to run lifecycle hooks around your tests.

Continuing the previous example, let's move the password hashing test inside a group.

import { test } from '@japa/runner'
import User from '#models/User'
import hash from '@adonisjs/core/services/hash'
test.group('creating user', () => {
test('hashes user password', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})
})

If you have noticed, we remove the "when creating a new user" fragment from our test title. This is because the group title clarifies that all tests under this group are scoped to creating a new user.

Lifecycle hooks

Lifecycle hooks are used to perform actions around tests. You can define hooks using the group object.

See also - Japa docs for Lifecycle hooks

test.group('creating user', (group) => {
group.each.setup(async () => {
console.log('runs before every test')
})
group.each.teardown(async () => {
console.log('runs after every test')
})
group.setup(async () => {
console.log('runs once before all the tests')
})
group.teardown(async () => {
console.log('runs once after all the tests')
})
test('hashes user password', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})
})

Next steps

Now that you know the basics of creating and writing tests. We recommend you explore the following topics in the Japa documentation.

Running tests

You may run tests using the test command. By default, the tests for all the suites are executed. However, you can run tests for a specific suite by passing the name.

node ace test
node ace test functional
node ace test unit

Watching for file changes and re-running tests

You may use the --watch command to watch the file system and re-run tests. If a test file is changed, then tests inside the changed file will run. Otherwise, all tests will be re-run.

node ace test --watch

Filtering tests

You can apply filters using the command-line flags when running the tests. Following is the list of available options.

See also: Japa filtering tests guide

Using VSCode? Use the Japa extension to run selected tests within your code editor using keyboard shortcuts or the activity sidebar.

FlagDescription
--testsFilter test by the test title. This filter matches against the exact test title.
--filesFilter tests by subset of test file name. The match is performed against the end of the filename without .spec.ts. You can run tests for a complete folder using the wildcard expression. folder/*
--groupsFilter test by group name. This filter matches against the exact group name.
--tagsFilter tests by tags. You can prefix the tag name with tilde ~ to ignore tests with the given tag
--matchAllBy default, Japa will run tests that matches any of the mentioned tags. If you want all tags to match, then use the --matchAll flag

Force exiting tests

Japa waits for the process to gracefully shut down after completing all the tests. The graceful shutdown process means exiting all long-lived connections and emptying the Node.js event loop.

If needed, you can force Japa to exit the process and not wait for a graceful shutdown using the --force-exit flag.

node ace test --force-exit

Retrying tests

You can retry failing tests for multiple times using the --retries flag. The flag will be applied to all the tests without an explicit retries count defined at the test level.

# Retry failing tests 2 times
node ace test --retries=2

Running failed tests from the last run

You can re-run tests failed from the last run using the --failed commandline flag.

node ace test --failed

Switching between reporters

Japa allows you register multiple test reporters inside the config file, but does not activate them by default. You can activate reporters either inside the config file, or using the --reporter commandline flag.

# Activate spec reporter
node ace test --reporter=spec
# Activate spec and json reporters
node ace test --reporter=spec,json

You may also activate reporters inside the config file.

export const reporters: Config['reporters'] = {
activated: ['spec', 'json']
}

Passing options to the Node.js commandline

The test command runs tests (bin/test.ts file) as a child process. If you want to pass node arguments to the child process, you can define them before the command name.

node ace --no-warnings --trace-exit test

Environment variables

You may use the .env.test file to define the environment variables required during testing. The values inside the .env.test takes precedence over those inside the .env file.

The SESSION_DRIVER during testing must be set to memory.

.env.test
SESSION_DRIVER=memory