test() - uhop/tape-six GitHub Wiki

test is the entry point to the test suite:

import test from 'tape-six';
// import {test} from 'tape-six';

// CommonJS (named import is recommended):
// const {test} = require('tape-six');

To help port tests from other frameworks, test() is aliased as suite(), describe() and it(). The aliases are exported from the package.

When called inside a test body, test() and its aliases automatically delegate to the current tester's t.test() method. The same applies to test.skip(), test.todo(), and test.asPromise(). Using t.test() directly is still preferred because it makes the delegation explicit.

Signatures

This function registers a test suite. Available options:

  • async test(name, options, testFn) — registers a test suite to be executed asynchronously. The returned promise is resolved when the test suite is finished.
    • In most cases no need to wait for the returned promise.
    • The test function has the following signature: async testFn(tester)
      • The test function can be async or sync. No need to return a promise.
  • test.skip(name, options, testFn) — registers a test suite to be skipped.
    • It is used to mark a test suite to be skipped. It will not be executed.
  • test.todo(name, options, testFn) — registers a test suite that is marked as work in progress.
    • Tests in this suite will be executed, errors will be reported but not counted as failures.
    • It is used to mark tests for incomplete features under development.
  • test.asPromise(name, options, testPromiseFn) — registers a test suite to be executed asynchronously using the callback-style API to notify that the test suite is finished.
    • The test function has a different signature: testPromiseFn(tester, resolve, reject).
      • The whole resolve/reject pair is modeled after the Promise API.
      • The resolve function is used to notify that the test suite is finished successfully.
        • The value passed to resolve() will be ignored.
      • The reject function is used to notify that the test suite is finished with an error.
        • The error passed to reject() will be reported as a failure.

All three arguments (name, options, testFn or testPromiseFn) are optional and can be used in any order (they are recognized by their types).

Arguments

The arguments mentioned above are:

  • name — the optional name of the test suite. If not provided, it will be set to the name of the test function or '(anonymous)'.
  • options — the optional options object. Available options:
    • name — the optional name of the test suite. If not provided, it will be set to the name of the test function or '(anonymous)'.
      • Can be overridden by the name argument.
    • testFn — the optional test function to be executed (see below).
      • Can be overridden by the testFn argument.
    • skip — if true, the test suite will be skipped.
    • todo — if true, the test suite will be marked as work in progress.
    • timeout — the optional timeout in milliseconds. It is used for asynchronous tests.
      • If the timeout is exceeded, the test suite will be marked as failed.
      • Important: JavaScript does not provide a generic way to cancel asynchronous operations. When the timeout is exceeded, tape6 will use signal property of the tester object to indicate that the test should be cancelled. See Tester for more details.
      • The default: no timeout.
    • Hooks (see below for details):
      • beforeAll — a function to be executed before all tests in the suite.
      • afterAll — a function to be executed after all tests in the suite.
      • beforeEach — a function to be executed before each test in the suite.
      • afterEach — a function to be executed after each test in the suite.
      • before — an alias for beforeAll.
        • If both are set, beforeAll will be executed before before.
      • after — an alias for afterAll.
        • If both are set, afterAll will be executed after after.
  • testFn — a test function to be executed. It will be called with the tester object. The result will be ignored.
    • This function can be synchronous or asynchronous.
  • testPromiseFn — a callback-based test function to be executed (see below). It will be called with the tester object and two callbacks: resolve and reject modeled on the Promise API.
    • Value supplied to resolve() will be ignored.
    • Value supplied to reject() will be used as the error message.

All hooks are functions with no arguments returning void or a Promise<void>:

() => void | Promise<void>;

Read more on hooks and their use in before and after hooks.

Examples

Given all that test and its helpers can be called like this:

test(name, options, testFn);
test(name, testFn);
test(testFn);
test(name, options);
test(options, testFn);
test(options);

// examples:
test('foo', t => {
  t.pass();
});
test('bar', async t => {
  t.fail();
});
test(function baz(t) {
  t.ok(1 < 2);
});
test({
  name: 'qux',
  todo: true,
  testFn: t => {
    t.ok(1 < 2);
  }
});

test.skip('foo1', t => {
  t.pass();
  t.fail();
});

test.todo('foo2', t => {
  t.pass();
  t.fail();
});

test.asPromise('foo3', (t, resolve, reject) => {
  const stream = fs.readStream('foo.txt');

  let buffer = '';
  stream.on('data', chunk => {
    buffer += chunk;
  });
  stream.on('end', () => {
    t.equal(buffer, 'foo');
    resolve();
  });
  stream.on('error', error => reject(error));
});

Embedded tests

The preferred way to embed tests is to use the test method of the tester object:

test('top', async t => {
  t.pass();
  await t.test('nested #1', async t => {
    t.pass();
  });
  await t.test('nested #2', async t => {
    t.pass();
    await t.test('even more nested', async t => {
      t.pass();
    });
  });
});

Since top-level test() (and its aliases describe(), it(), suite()) automatically delegate to the current tester when called inside a test body, the following is equivalent:

test('top', async t => {
  t.pass();
  await test('nested #1', async t => {
    t.pass();
  });
  await test('nested #2', async t => {
    t.pass();
    await test('even more nested', async t => {
      t.pass();
    });
  });
});

Using t.test() is preferred because it makes the delegation explicit. The top-level form is convenient when porting tests from frameworks like Mocha or Jest that use describe()/it() without a tester object.

The same delegation applies to test.skip(), test.todo(), and test.asPromise().

Asynchronous tests

test() registers a test that will be executed asynchronously later. It returns a promise that resolves when the test is completed. Generally tests will be executed in the order of their registration in the file. In most cases there is no need to wait for the test to complete unless you want to analyze the results of the test.

The situation is totally different for embedded tests because it may affect the order of execution.

Let's start with a simple synchronous test:

test('foo', t => {
  t.pass();
});

As you can see we don't mark our test as asynchronous and everything works as expected.

Now let's add an asynchronous embedded test:

test('bar', t => {
  t.pass('pass #1');
  t.test('nested', t => {
    t.pass('nested pass');
  });
  t.pass('pass #2');
});

Now if we run the test the result will be not exactly what we expected:

○ bar
  ✓ pass #1 - 0.856ms
  ✓ pass #2 - 0.041ms
  ○ nested
    ✓ nested pass - 0.05ms
  ✓ nested  1  0  - 0.157ms
✓ bar  3  0  - 1.877ms

You can see that the order of execution is not what we expected: nested should be executed before pass #2 yet we can clearly see that it is executed after pass #2.

If we make this test more elaborate:

test('bar', t => {
  t.pass('pass #1');
  t.test('nested #1', t => {
    t.pass('nested #1 pass');
  });
  t.pass('pass #2');
  t.test('nested #2', t => {
    t.pass('nested #2 pass');
  });
  t.pass('pass #3');
});

The result will be:

○ bar
  ✓ pass #1 - 0.894ms
  ✓ pass #2 - 0.051ms
  ✓ pass #3 - 0.011ms
  ○ nested #1
    ✓ nested #1 pass - 0.059ms
  ✓ nested #1  1  0  - 0.169ms
  ○ nested #2
    ✓ nested #2 pass - 0.048ms
  ✓ nested #2  1  0  - 0.116ms
✓ bar  5  0  - 2.217ms

As you can see all "synchronous" checks are executed before any asynchronous tests. All asynchronous tests are executed at the end of an outer test in the order of their declaration. The outer test (bar) didn't finish until all embedded tests were executed. Every embedded test has waited for the previous test, if any, to finish.

Most probably it is not what you expected. How to fix it? Use async and await properly:

test('bar', async t => {
  t.pass('pass #1');
  await t.test('nested #1', t => {
    t.pass('nested #1 pass');
  });
  t.pass('pass #2');
  await t.test('nested #2', t => {
    t.pass('nested #2 pass');
  });
  t.pass('pass #3');
});

The result:

○ bar
  ✓ pass #1 - 0.866ms
  ○ nested #1
    ✓ nested #1 pass - 0.077ms
  ✓ nested #1  1  0  - 0.375ms
  ✓ pass #2 - 0.561ms
  ○ nested #2
    ✓ nested #2 pass - 0.039ms
  ✓ nested #2  1  0  - 0.123ms
  ✓ pass #3 - 0.187ms
✓ bar  5  0  - 2.156ms

Now this is what we really wanted.

⚠️ **GitHub.com Fallback** ⚠️