Test class
Tests created using the test
method are instances of the Test class. You can invoke the following 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 determine whether the test should be skipped.
// 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()
})
You can also define the reason for skipping the test as follows.
test('add two numbers', () => {
})
.skip(true, 'Cannot run it in CI')
fails
Mark the 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, currently 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 is not complete within 6 seconds.
test('add two numbers', () => {
})
.timeout(6000)
disableTimeout
Disable timeout for a given test.
test('add two numbers', () => {
})
.disableTimeout()
resetTimeout
Reset the timeout duration of the test. You can also call this method within the test callback.
test('get payments list', (ctx) => {
ctx.test.resetTimeout(60 * 10000)
await getPaymentsList()
})
.timeout(2000)
tags
Assign tags to a test. You can later use tags to filter and run only specific tests.
See also: Tagging tests
test('add two numbers', () => {
})
.tags(['@slow', '@network'])
The method accepts an optional strategy for defining tags. The strategy can be replace
, append
, or prepend
. Defaults to replace
.
// append tags
test('add two numbers', () => {
})
.tags(['@network'], 'append')
// prepend 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)
waitForDone
You can run asynchronous operations inside the test implementation function using async/await
. For example:
See also: Testing asynchronous code
test('add two numbers', async () => {
await someAsyncOperation()
})
However, there can be cases when you cannot use async/await
, especially when dealing with streams and events.
Therefore, you can use the waitForDone
method to instruct Japa to wait until an explicit call to the done
method is made. For example:
test('add two numbers', async (ctx, done) => {
emitter.on('someEvent', () => {
done()
})
})
.waitForDone()
pin
Pin the test. When one or more tests are pinned, Japa will execute only the pinned tests and skip all others.
See also: Pinned tests
test('add two numbers', () => {
})
.pin()
with
Define the dataset for the test. The dataset must be an array, and the test will execute for each item in the array.
See also: Datasets
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.
See also: Hooks
test('add two numbers', () => {
})
.setup(async () => {
// executed before the test
})
teardown
Define the teardown hook for the test.
See also: 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 the ctx.test
property.
test('add two numbers', (ctx) => {
console.log(ctx.test.options)
})
/**
{
title: string
tags: string[]
timeout: number
meta: {
suite: Suite,
group: Group,
fileName: string
}
retries?: number
executor?: TestExecutor<any, any>
isTodo: false
}
*/
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)
})
isPinned
A property to know if the test is pinned using the pin
method.
test('add two numbers', (ctx) => {
console.log(ctx.test.isPinned)
})
Extending Test class
You can add custom properties to the Test
class using macros and getters. Macros and getters offer an API to add properties to the prototype of a class. You can think of them as Syntactic sugar for Object.defineProperty
. Under the hood, we use macroable package, and you can refer to its README for an in-depth technical explanation.
You can define custom properties inside the Japa entry point file (i.e., bin/test(.js|.ts)
).
import { Test } from '@japa/runner/core'
Test.macro('isSlow', function () {
this.tags(['@slow'], 'append')
return this
})
Once you have defined the isSlow
method, you can call it as follows on the test
object.
import { test } from '@japa/runner'
test('get users list', () => {
})
.isSlow()
Defining TypeScript types
If you use TypeScript, you must notify its compiler about the newly added property using declaration merging.
Again, you may write the following code within the Japa entry point file.
declare module '@japa/runner/core' {
interface Test {
isSlow(): this
}
}