0.32.3 • Published 1 year ago

qnit v0.32.3

Weekly downloads
23
License
Apache-2.0
Repository
github
Last release
1 year ago

qnit

simple unit test runner, in the spirit of nodeunit and phpunit

Summary

Nodeunit-like command-line usage:

npm install -g qnit
qnit [testdir|testfile] [...]

where testdir/testfile.js contains for example:

module.exports = {
    'should run test': function(t) {
        t.printf("running test!\n");
        t.done();
    },
};

Command Line

qnit [options] [file] [file2 ...]

Runs the unit tests in the specified files, or all tests in the ./test directory.

Options:

  • -C - do not colorize console output. Piped or redirected output is never colorized.
  • --config - read additional run-time options from the named file
  • -f PATT, --filter PATT - only run tests whose full name contains the string PATT
  • --fork-files - run each test file in a separate process
  • -h, --help - show built in usage
  • --no-exit - do not call process.exit() when done, wait for a clean shutdown
  • --no-recurse - do not recurse into and run tests in test sub-directories
  • -r PACKAGE, --require PACKAGE - load the package before starting the tests
  • --stop-on-failure, -b, --bail - do not continue with the test suite if one of the tests fails
  • -s MS, --slow MS - ms elapsed test time before reporting duration (default 0, all tests)
  • -t MS, --timeout MS - ms idle timeout to wait for a test to call done() (default 2000)
  • -V, --version - print the qnit version and exit

Options can also be specified in a file qnit.conf in the test directory in whitespace-delimited command-line syntax (newlines are treated as spaces).

Nodeunit Compatibility

Nodeunit runs the test exported via module.exports. The test can be a test function or an object containing test functions and/or test objects. Functions are invoked as tests, objects get their properties walked, with function properties invoked as tests, and object properties recursed into.

Each test function gets a newly created empty object that is set to this when the test is run. The test function is passed a single parameter, the tester object (usually called t), having a special method done. t.done() must be called for the test to succeed.

Each test object can have two special properties, setUp and tearDown. All other properties are test functions or nested test objects. If the setUp function exists, it will be called before every contained test function, contained both directly or indirectly inside a nested test object. setUp sees the same this that the test function will see; changes made to properties this.x in setUp will be visible inside the test function and also in tearDown. tearDown is similar to setUp but is called after the test calls done().

Note: unlike Mocha, Nodeunit runs every enclosing setUp method for each test run, it does not distinguish top-level global initialization from test-specific local initialization. Using Mocha terminology, setUp and tearDown correspond to beforeEach and afterEach. Qnit supports both before and beforeEach as well as setUp.

Each test function when it runs invokes all setUp and tearDown calls from all enclosing objects. setUp and tearDown functions are paired and nest around the test: a new this object is created, the setUp functions are called in outermost to innermost order, the test function is run, then the tearDown functions are run in the innermost to outermost order.

Example

var assert = require('assert');
module.exports = {
    setUp: function(done) {
        this.x = 1;
        done();
    },
    tearDown: function(done) {
        this.x = null;
        done();
    },
    'test function': function(t) {
        assert.equal(this.x, 1);
        assert.equal(this.y, undefined);
        t.done();
    },
    'nested tests': {
        setUp: function(done) {
            this.y = 2;
            done();
        },
        'nested test function': function(t) {
            assert.equal(this.x, 1);
            assert.equal(this.y, 2);
            t.done()
        },
    },
};

Mocha Compatibility

Mocha installs global functions describe, it, before, after, beforeEach and afterEach that build up a test structure very similar to that of nodeunit.

describe starts a new test object, and is passed a callback that builds the test hierarchy. describe can contain it tests or nested desribe's. it specifies a test to run; it is passed a label and the test function itself. before and after pair and are called before and after the tests in the current test object, respectively. beforeEach and afterEach are called before and after every it test.

Note: unlike Nodeunit, before and after are run just once for each nested describe, not once for each nested test. This allows expensive setup to be run only once and be reused by multiple tests. Like Nodeunit, beforeEach and afterEach methods are invoked for every it test, both those in the current describe and those nested in contained describe's.

State is shared between the tests and the setup/teardown methods via closures. The enclosing describe must declare the shared variables for them to be properly scoped.

Example:

var assert = require('assert');
describe(function() {
    var x;
    beforeEach(function(done) {
        x = 1;
        done();
    });
    afterEach(function(done) {
        x = null;
        done();
    });
    it('test function', function(done) {
        assert.equal(x , 1);
        done();
    });
    describe('nested tests', function() {
        var y;
        beforeEach(function(done) {
            y = 2;
            done();
        });
        it('nested test function', function(done) {
            assert.equal(x, 1);
            assert.equal(y, 2);
            done();
        });
    });
});

Tester Methods

The tester object passed in to the unit test functions has a number of useful methods, the most important of which is done.

myTest: function(t) {
    t.done();
}

t.done( )

callback that must be called when the test finishes. If not called, the test will fail (error out), and the tearDown methods will not be called.

t.expect( count )

When test function is complete, require that the number of assertions run be equal to count. The test will fail if the number of assertions made differs from the expected assertion count.

TODO: expect does not wait any additional time for the test to complete

t.ok( condition, message )

assert that the condition is truthy, else fail the test. Also available as t.assert()

t.fail( )

Fail the test. fail() is not counted as an assertion, it's an outright failure.

t.skip( )

Skip this test, neither failed nor succeeded.

t.printf( format, arg1, ... )

quick little printf-like output formatter from the qprintf package. Interpolates the arguments into the format string and writes them to process.stdout. Recognizes more formats than console.log, and is easier to type. The intent is to have a tracer call that can run asynchronously and not add much overhead to the test being timed, in case the test is timing sensitive (TODO: verify in practice)

printf supports the core set of conversions plus %O for objects. See the qprintf Readme for full details.

Examples

("%5d", 123)            => "  123"
("0x%04x", 123)         => "0x007b"
("%10s", "Hello")       => "     Hello"
("%-10s", "Hello")      => "Hello     "

Tester Assertions

The assertions from the assert module (and the enhancements from qassert) are available as tester methods. If the assertion fails, an exception is thrown that qnit catches and reports as a failed unit test. The rest of that test function is omitted, and the next test is run (unless --stop-on-failue was specified on the command line). Tests that fail an assertion will immediately stop, will not run any more assertions, and will not have their tearDown methods called.

All assertions accept an optional message. If provided, the message will be included in the assertion diagnostics. It can be helpful to explain what failed. Note that qnit includes both the assert failure message and the user-provided message; assert omits its diagnostic showing the failed values if the user had provided their own message.

t.assert( condition, message )

assert that the condition is truthy, else fail the test. Same as t.ok().

t.equal( a, b, message )

coercive equality test, a == b. Like in nodeunit and phpunit, this assertion is also available by the alias equals.

t.notEqual( a, b, message )

coercive inequality, a != b

t.deepEqual( a, b, message )

recursive equality, objects and arrays have equal elements. Element comparisons are coercive ==, thus [1] and [true] are deepEqual.

t.notDeepEqual( a, b, message )

recursive inequality, objects and arrays differ.

t.strictEqual( a, b, message )

strict equality test, a === b

t.notStrictEqual( a, b, message )

strict inequality test, a !== b

t.contains( a, b, message )

coercive test that a contains b as substring or a matches regex b (strings), array element (array and non-array), subarray (arrays) or object key-value subset (objects). If not arrays, objects or strings, a and b will be compared directly.

t.strictContains( a, b, message )

inclusion test as above but with elements compared pairwise with strict-equals ===.

t.notContains( a, b, message )

like contains but with the sense reversed, the test fails if a contains b

t.notStrictContains(a, b, message )

like strictContains but with the sense reversed, the test fails if a strictContains b

t.within( a, b, distance, message )

test that the number a is within +/- distance of b

t.inorder (arr, compar, )

test that the elements of the array are in ascending order, as determined by the comparison function. The default comparison test is (a,b) => a > b ? 1 : 0.

t.throws( block, error, message )

confirm that the block throws the specified error. Error can be an Error object, a RegExp, or a validator function.

t.doesNotThrow( block, message )

confirm that the block does not throw an error.

t.ifError( err )

fail the test if the error is set

Tester Mocks

QUnit supports mock test doubles using the qmock library. Refer to qmock for details.

t.getMock( object, methodsToMock, constructorArgs )

Create a test double. object can be an existing object or a constructor function. The mock double will be instanceof the same class and having the same methods. The methodsToMock list of methods will be stubbed out and replaced with no-op methods. constructorArgs will be used if object is a constructor function.

t.getMockSkipConstructor( constructor, methods )

Similar to getMock, but does not run the constructor function. The mock double will be instanceof the constructor and will inherit the same methods as a new instance. If constructor is an object (not a constructor function), the call behaves like getMock.

t.stub( func )

t.stub( object, methodName ,overrideFunc )

t.spy( func )

t.spy( object, methodName )

Instrument calls to the given function or the named method. If overrideFunc is specified, also replace the method with the override. Returns a spy function that holds the gathered stats. Restore the original method back onto the object with spy.restore().

Stub instruments and overrides; spy tracks details about the first 10 calls made.

For details, see qmock.

t.spy( func )

Return an instrumented copy of the function. If no func is specified, creates an anonymous function. The call stats accessible as func.stub. For details, see qmock.

t.mockTimers( )

t.unmockTimers( )

Replace the system timers functions setImmediate, setTimeout etc with mock doubles where elapsed time is controlled by the test. Use unmockTimers() to restore the original system timers.

Returns a clock object with a method clock.tick that advances time:

  • clock.tick(0) - advance to the next event loop, run any pending immediate tasks
  • clock.tick() - advance time by 1 millisecond, run immediates and any timeouts
  • clock.tick(N) - advance time by N milliseconds, run immediates and any timeouts that come due at any point during the "elapsed" time, including those queued by other immediates or timeouts.

For details, see qmock.

t.mockRequire( moduleName, replacement )

t.mockRequireStub( moduleName, handlerFunction )

t.unmockRequire( moduleName )

t.unrequire( moduleName )

Arrange for require() of the named module to return the replacement instead. Use repeated mockRequire calls to mock multiple modules. Use unmockRequire to unhook the module mocks, restore the system built-in require functionality and clear all mocked modules.

For details, see qmock.

t.mockHttp( handler(req, res) )

t.mockHttp( )

t.unmockHttp( )

Replace http.request and https.request with calls to the handler. The handler is responsible for emulating the expected behavior. The req and res passed to handler are instances of http.ClientRequest used to make an http request to the server and http.IncomingMessage that the client receives with the server response.

As of qnit 0.14.0, this feature is experimental.

For more details, see qmock.

Related

  • mocha
  • nodeunit - phpunit-like unit tests
  • nyc - command-line coverage analyzer, works well with qnit
  • qassert - assert library wrapper built into qnit
  • qmock - qmock library built into qnit
  • qprintf - printf library built into qnit

Todo

  • bundle up errors and output all at the end (instead of interleaving)
  • gather result rows into json and output with a json-to-text reporter module
  • --cov code coverage reporting using istanbul, nyc or some such
  • mocha compat: make before/after callback optional
0.32.3

1 year ago

0.32.2

1 year ago

0.31.0

3 years ago

0.30.0

3 years ago

0.28.0-d

4 years ago

0.27.0

4 years ago

0.26.0

5 years ago

0.25.2

5 years ago

0.25.1

6 years ago

0.25.0

6 years ago

0.24.0

6 years ago

0.23.1

6 years ago

0.23.0

6 years ago

0.22.1

6 years ago

0.22.0

6 years ago

0.21.0

6 years ago

0.20.0

6 years ago

0.19.0

6 years ago

0.18.3

6 years ago

0.18.2

6 years ago

0.18.1

7 years ago

0.18.0

7 years ago

0.17.1

7 years ago

0.17.0

7 years ago

0.16.2

7 years ago

0.16.1

7 years ago

0.16.0

7 years ago

0.15.3

7 years ago

0.15.2

7 years ago

0.15.1

7 years ago

0.15.0

7 years ago

0.14.5

7 years ago

0.14.2

7 years ago

0.14.0

7 years ago

0.13.1

7 years ago

0.13.0

8 years ago

0.12.2

8 years ago

0.12.1

8 years ago

0.12.0

8 years ago

0.11.0

8 years ago

0.10.0

8 years ago

0.9.0

8 years ago

0.8.0

8 years ago