How to write doctests

Published at: April 4, 2018, 3:36 p.m. in the Documentation

Doctestes play an important role in creating examples for documentation, as well as for testing the functions of your API.

How to run

Writing the tests from the "api.js" file is very easy to run:

% smappi test

To debug code running with tests, you can use inspect, as well as to debug API:

% node --inspect `which smappi` test

How to write

For make it easier to write the docs, we have improved the jsdoctap library for working with masks.

Masks

With the help of the mask operator "..." you can match any data, this will reduce the detailed description of the test and make the examples understandable and short.

In addition, if you parse sites or other sources, then usually some of the data changes and you will need to adjust to it, the masks will allow you to ignore the data being changed.

Lists

For lists, use the "...[]" mask correctly, but you can also "...":

/**
 * Array
 *
 * @example
 *   func(1, 2, 3)
 *   // => [1, 2, 3, ...[], 8, 9]
 *   func(1, 2, 3)
 *   // => [1, 2, 3, ..., 9]
 */
function func (a, b, c) {
    return [a, b, c, 4, 5, 6, 7, 8, 9]
}

Objects

For objects, use the "...{}" mask correctly:

/**
 * Object
 *
 * @example
 *   func()
 *   // => {a: 1, ...{}, z: 42}
 */
function func () {
    return {a: 1, b: 2, c: 3, z: 42}
}

Strings

Strings can be matched by the mask "..." or using a regular expression:

/**
 * String
 *
 * @example
 *   func()
 *   // => "Hello ..."
 *
 * @example
 *   func()
 *   // => /Hello.+/
 */
function func () {
    return "Hello World"
}

Numbers

Numbers can be matched using a regular expression:

/**
 * Number
 *
 * @example
 *   func()
 *   // => /\d+/
 */
function func () {
    return 42
}

Mixed example

Example with all types of data at once:

/**
 * Mixed
 *
 * @example
 *   func()
 *   // => [1, 2, 3, ...[], {a: 1, b: 2, c: /\d+/, ...{}, z: 'Hello ...'}, 7, 8, ...]
 */
function func () {
    return [
        1, 2, 3, 4, 5, 6, {
            a: 1,
            b: 2,
            c: 3,
            d: 4, e: 5,
            z: 'Hello World'
        },
        7, 8, 9, 10
    ]
}

Customization

If you do not have this syntax, you can always wrap the function call and change the response:

/**
 * Delete one property from object
 *
 * @example
 *   (function () { let res = func(); delete res.age; return res; })()
 *   // => {name: 'John'}
 */
function func () {
    return {name: 'John', age: 42}
}

Deferred return

If you use a deferred return, then the tests will wait for it to run, so you do not have to worry about it. However, the tests in this case will work asynchronously, i.e. not in the order in which the file "api.js" is defined:

/**
 * Deferred return example
 *
 * @example
 *   func()
 *   // => {age: 42}
 */
function func () {
    setTimeout(() => {
        self.return({age: 42})
    }, 2000);
}

More examples can be found here