Underlying Test class

Tests created using the test method are instances of the Test class. You can invoke the available class methods to configure the test further.

In this guide we will go through all the methods and properties available on the test class.

skip

Mark the test as skipped. You can either pass a boolean to the skip method or a function that is lazily evaluated to find if the test should be skipped or not.

// Skip the test
test('add two numbers', () => {
})
.skip(true)
// Skip conditionally
test('add two numbers', () => {
})
.skip(!!process.env.CI)
// Skip lazily
test('add two numbers', () => {
})
.skip(() => {
return findIfShouldBeSkipped()
})

The 2nd optional argument is the reason for skipping the test.

test('add two numbers', () => {
})
.skip(true, 'Not running in CI')

fails

Mark test as a regression test. Regression tests are meant to fail.

A great example of a regression test is someone reporting a bug with a failing test, and once the bug has been fixed, you can remove the call to the fails method.

test('add two numbers', ({ assert }) => {
assert.equal(add(2, 2), 4)
})
.fails('add method should return 4, instead it returns 5')

timeout

Define the timeout for the test. By default, the config.timeout value is used. However, you can override it directly on the test.

In the following example, the test will be marked as failed if it does not complete within 6 seconds.

// 6 seconds
test('add two numbers', () => {
})
.timeout(6000)

disableTimeout

Disable timeout for a given test.

test('add two numbers', () => {
})
.disableTimeout()

tags

Assign tags to a test. You can later use tags to filter and run only specific tests. Learn more about tags.

test('add two numbers', () => {
})
.tags(['@slow', '@network'])

The method accepts an optional strategy for defining tags. The strategy can be either replace, append, or prepend.

test('add two numbers', () => {
})
.tags(['@slow'])
// Append to existing tags
test('add two numbers', () => {
})
.tags(['@network'], 'append')
// Prepend to existing tags
test('add two numbers', () => {
})
.tags(['@network'], 'prepend')

retry

Define the retry count for the test. Only after the mentioned retries will the test be marked as failed.

In the following example, the test will run four times. First is the original attempt, and then three retries before it is marked as failed.

test('add two numbers', () => {
})
.retry(3)

The spec reporter prefixes the test title with the current retry attempt
The spec reporter prefixes the test title with the current retry attempt

waitForDone

You can run asynchronous operations inside the test implementation function using async/await. For example:

test('add two numbers', async () => {
await someAsyncOperation()
})

However, there can be cases when you will not be able to use async/await, especially when dealing with streams and events.

Therefore, you can make use of the waitForDone method to instruct Japa to wait until an explicit call to the done method is made. For example:

The test will timeout if the done method is never called.

test('add two numbers', async (ctx, done) => {
emitter.on('someEvent', () => {
done()
})
})
.waitForDone()

You can also pass an error object to the done method to mark the test as failed.

test('add two numbers', (ctx, done) => {
stream.on('error', (error) => done(error))
})
.waitForDone()

pin

Pin the test. When one or more tests are pinned, Japa will execute only the pinned tests and skips all others. Learn more about pinned tests.

test('add two numbers', () => {
})
.pin()

with

Define the dataset for the test. The dataset must be an array, and we will execute the test for each item in the array. Learn more about datasets.

We recommend using the run method instead of passing the callback as the 2nd argument.

The run method called after the with method has added the benefit of type safety in TypeScript projects.

test('validate email')
.with(['virk@adonisjs.com', 'foo@bar.com'])
.run(async (ctx, email) => {
// executed for each email
})

You can fetch the dataset lazily by defining a function.

async function getTestEmails () {
return ['virk@adonisjs.com', 'foo@bar.com']
}
test('validate email')
.with(getTestEmails)
.run(async (ctx, email) => {
// executed for each email
})

setup

Define the setup hook for the test. Learn more about hooks.

test('add two numbers', () => {
})
.setup(async () => {
// executed before the test
})

teardown

Define the teardown hook for the test. Learn more about hooks.

test('add two numbers', () => {
})
.teardown(async () => {
// executed after the test
})

options

A reference to the properties defined on the test. You can access the test instance within the test callback and hooks using ctx.test.

test('add two numbers', (ctx) => {
console.log(ctx.test.options)
})
// returns
{
title: string
tags: string[]
timeout: number
meta: {}
waitsForDone?: boolean
executor?: TestExecutor<any, any>
isTodo?: boolean
isSkipped?: boolean
isFailing?: boolean
skipReason?: string
failReason?: string
retries?: number
}

dataset

Reference to the test dataset. This property is defined when the test.execute method is called.

test('add two numbers', (ctx) => {
console.log(ctx.test.dataset)
})

context

Reference to the test context.

test('add two numbers', (ctx) => {
console.log(ctx.test.context === ctx)
})

Extending Test class

The Test class is extensible by nature. You can use the Macros and Getters to add custom properties to it.

You can write the code for extending the Test class within the bin/test.js file or create a new file and import it inside the bin/test.js file.

About getters and macros

Getters and macros expose the API to add custom properties to the test class.

A getter is a function that is evaluated lazily when accessing the property.

import { Test } from '@japa/runner'
Test.getter('foo', function () {
return { foo: true }
})
console.log(test('test title').foo)
// returns { foo: true }

Whereas macros can be both functions and literal values. You can access the test instance using this.

Test.macro('recordTime', function () {
this.options.createdAt = new DateTime()
})
test('test title').recordTime()

Usage with TypeScript

Since getters and macros are added at runtime, you must inform the TypeScript compiler about these new properties separately.

You can make use of module augmentation to define these properties at a static level.

Create a new file, bin/japaTypes.ts, and paste the following code inside it.

declare module '@japa/runner' {
// Interface must match the class name
interface Test {
throws(error?: ErrorConstructor, message?: string): this
}
}