The global intern
object is an instance of
Executor.
It is automatically created when Intern is imported or loaded via a script tag.
This object is also the default export from the intern package:
import intern from 'intern';
The configure method is used to configure the executor. It should be passed an object containing configuration properties.
intern.configure({
suites: 'build/tests/**/*.js',
environments: 'chrome'
});
configure
may be called multiple times before testing is started. The final
configuration will be
resolved
before any plugins or suites are loaded.
The getPlugin method returns any resources exported by a registered plugin. The main purpose of this method is to allow resources to be accessed in environments without a loader. For example, importing an interface with
import { suite, test } from 'intern/lib/interfaces/tdd';
will not work in a browser environment where a module loader is not available,
but getPlugin
will work in any environment since it doesn't depend on a
loader.
const { suite, test } = intern.getPlugin('interfaces.tdd');
getPlugin
is a generic function, so custom plugins can be typed:
import { PluginType } from 'tests/support/myPlugin';
const { foo, bar } = intern.getPlugin<PluginType>('myPlugin');
In the above example, the PluginType
type is imported from a plugin and used
for typing when retrieving plugin resources from Intern.
The on method is used to register listeners for Intern events.
intern.on('testStart', test => {
console.log(`${test.id} has started`);
});
A listener listens for a certain event and is called with a specific payload.
For example, listeners for testStart
and testEnd
events will be called with
an instance of Test (or a Test-like object).
Intern provides a number of events that code can listen for:
Event | Emitted when... | Argument |
---|---|---|
error | An error (not a test failure) has occurred while running tests. | error |
runStart | Testing begins | none |
runEnd | Testing ends | none |
suiteStart | A suite starts. A suite with no parent indicates the start of a new session. | suite |
suiteEnd | A suite ends | suite |
testStart | A test starts | test |
testEnd | A test ends. Check the error , skipped , and hasPassed properties for status. |
test |
💡That the payloads passed to event listeners may or may not be actual instances of a particular class. For example, the
testEnd
listener may be passed a Test-like object rather than an actual instance of Test.
Event listeners may be asynchronous.
intern.on('testStart', test => {
return new Promise(resolve => {
const db = openDbConnection();
// An async method to write to a db
db.put(test, () => {
resolve();
});
});
});
Multiple listeners may register for an event. They will be called sequentially in the order in which they were registered.
registerPlugin, as it's name suggests, is used to register an extension with Intern. It's signature is
registerPlugin(name: string, init: PluginInitializer): void;
A
PluginInitializer
is just a callback that takes an optional options
argument and returns any
exported resources, or a Promise that resolves to resources.
intern.registerPlugin('foo', options => {
return {
doSomething() {
// ...
},
doSomethingElse() {
// ...
}
};
});
Since an initializer may return a promise, it can also be used to initialize asynchronous code during Intern's initialization process;
intern.registerPlugin(
'myplugin',
() =>
new Promise(resolve => {
asyncFunction.then(resolve);
})
);
The run method initiates the testing process. It returns a promise that resolves when testing is complete. It will reject if there is an error during the testing process or if any tests fail.
Interfaces are the primary way that test writers interact with Intern. Intern provides 4 interfaces: object, bdd, tdd, and benchmark.
All interfaces support the same set of lifecycle methods:
- before - Run before any tests
- after - Run after all tests
- beforeEach - Run before each test
- afterEach - Run after each test
const { registerSuite } = intern.getPlugin('interface.object');
The object interface allows test suites to be created declaratively using a plain JavaScript object. Simple suites can just contain an object of test functions:
registerSuite('Suite name', {
test1() {
// do something
},
test2() {
// do something
}
});
Suites may also register lifecycle callbacks (beforeEach
, afterEach
, etc.).
When these are used, tests must be contained under a tests
property.
registerSuite('Suite name', {
beforeEach() {
// test setup
},
tests: {
test1() {
// do something
},
test2() {
// do something
}
}
});
Suites may be nested.
registerSuite('Suite name', {
test1() {
// do something
},
test2() {
// do something
},
'sub-suite': {
subTest1() {
// do something
}
subTest2() {
// do something
}
}
});
const { suite, test, beforeEach } = intern.getPlugin('interface.tdd');
The tdd interface is callback-driven.
suite('Suite name', () => {
beforeEach(() => {
// test setup
});
test('test1', () => {
// do something
});
test('test2', () => {
// do something
});
});
This interface is a bit more flexible than the object interface. For example, it allows multiple instances of a given lifecycle method to be registered. This can be useful when setting up shared functionality between suites:
function initSuite() {
beforeEach(() => {
// do common init
});
}
suite('Suite', () => {
initSuite();
beforeEach(() => {
// suite init
});
test('test1', () => {
// ...
});
});
suite('Other suite', () => {
initSuite();
test('test1', () => {
// ...
});
});
const { describe, it, beforeEach } = intern.getPlugin('interface.bdd');
The bdd
interface is identical to the tdd interface, it just renames the suite
and
test
methods to describe
and it
.
describe('Thing', () => {
beforeEach(() => {
// test setup
});
it('should do A', () => {
// do something
});
it('should do b', () => {
// do something
});
});
const { registerSuite } = intern.getPlugin('interface.benchmark');
The benchmark interface is modified version of the object interface used to run performance benchmark tests. The two differences from object are:
- Async tests must be wrapped in an
async
function as the standard mechanisms for handling async code don't work with benchmark tests. Note that this is not the same as theasync
keyword. - Two additional lifecycle methods are supported:
beforeEachLoop
andafterEachLoop
. A benchmark suite will call each test function many times in a row to evaluate average performance. The standardbeforeEach
andafterEach
run before and after a benchmark begins. The*Loop
variations are run before and after each call of the test function.
registerSuite('Suite name', {
beforeEach() {
// test setup
},
beforeEachLoop() {
// test function call setup
},
tests: {
test1() {
// do something
},
test2: async(dfd => {
// do something
})
}
});
Intern includes the chai assertion library, available to suites as the 'chai' plugin.
const { assert } = intern.getPlugin('chai');
registerSuite('Suite name', {
test1() {
assert.isNotNull(someValue);
}
});
All three interfaces (assert, expect, and should) are exported by the chai plugin.
The Suite class manages a group of tests. It provides several properties and methods that may be useful during testing and in reporters.
The error property is set when a suite experienced an error. It can be used in reporters to determine whether a suite has failed.
intern.on('suiteEnd', suite => {
if (suite.error) {
console.log(`${suite.id} failed: ${suite.error}`);
}
});
The id property is the complete ID of a suite.
intern.on('suiteEnd', suite => {
console.log(`${suite.id} finished`);
});
The name property is the short name of a suite. This can be useful for reporters that generate structured reports, where a suite doesn‘t need to be referred to by its complete ID.
intern.on('suiteEnd', suite => {
console.log(`${suite.name} finished`);
});
The remote property is an instance of a Leadfoot Command object that is used to access a remote browser in functional test suites.
registerSuite('Suite', {
before() {
// Load a page before any tests start
return this.remote.get('page.html');
}
});
The skip method is called to skip a suite. Any tests that have not been run will be marked as skipped.
registerSuite({
before() {
if (condition) {
this.skip('Skipping because ...');
}
}
});
The skipped property is set when a suite has been skipped. It can be used in reporters to determine whether a suite has been skipped.
intern.on('suiteEnd', suite => {
if (suite.skipped) {
console.log(`${suite.id} was skipped: ${suite.skipped}`);
}
});
The timeElapsed property indicates the time required for the test to run in ms.
intern.on('suiteEnd', suite => {
console.log(`${suite.id} ran in ${suite.timeElapsed} ms`);
});
The Test class represents a single test. It provides several properties and methods that may be useful during testing and in reporters.
Call the async method in a test to return a Deferred object that can be used to manage an async test, and/or to adjust the test timeout.
test1() {
// Test will timeout in 500ms
const dfd = this.async(500);
someAsyncOperation(error => {
if (error) {
dfd.reject(error);
} else {
dfd.resolve();
}
});
});
The error property is set when a test experienced an error. It can be used in reporters to determine whether a test has failed.
intern.on('testEnd', test => {
if (test.error) {
console.log(`${test.id} failed: ${test.error}`);
}
});
The hasPassed property is set when a test has passed. It can be used in reporters to determine whether a test was run successfully.
intern.on('testEnd', test => {
if (test.hasPassed) {
console.log(`${test.id} passed`);
}
});
The id property is the complete ID of a test.
intern.on('testEnd', test => {
console.log(`${test.id} finished`);
});
The name property is the short name of a test. This can be useful for reporters that generate structured reports, where a test doesn‘t need to be referred to by its complete ID.
intern.on('testEnd', test => {
console.log(`${test.name} finished`);
});
The remote property is an instance of a Leadfoot Command object that is used to access a remote browser in functional tests.
registertest('test', {
test1() {
return this.remote
.get('page.html')
.findByCssSelector('.button')
.click();
}
});
The skip method is called to skip a test.
registertest({
test1() {
if (condition) {
this.skip('Skipping because ...');
}
}
});
The skipped property is set when a test has been skipped. It can be used in reporters to determine whether a test has been skipped.
intern.on('testEnd', test => {
if (test.skipped) {
console.log(`${test.id} was skipped: ${test.skipped}`);
}
});
The timeElapsed property indicates the time required for the test to run in ms.
intern.on('testEnd', test => {
console.log(`${test.id} ran in ${test.timeElapsed} ms`);
});
Set the timeout property in a test to adjust the maximum time the test may take to run. If the test exceeds this time, it will fail. The default timeout is 30000 ms.
test1() {
this.timeout = 500;
// test
}
}