mocha-skip-if v1.0.4
Mocha-skip-if
This module enables you to selectively skip certain Mocha tests based on runtime conditions. It'll call it.skip()
or describe.skip()
when the expressed conditions are met and it()
or describe()
otherwise.
Basic Usage
require('mocha-skip-if');
skip.condition({
watching: /:watch/.test(process.env.npm_lifecycle_event),
debugging: (part) => (part === process.env.DEBUGGING),
})
describe('Test', function() {
skip.if.watching.and.not.debugging('data retrieval').
describe('Remote data retrieval', function() {
// activities subjected to rate-limit
})
})
This module adds the global variable skip
. You call its condition()
method to define conditions. In the example above, two conditions are defined: watching and debugging. The latter requires one argument.
Once conditions are defined, they become available as properties of skip.if
and logical operators like and
and not
. You can then construct skip statements in a style similar to the one used by Chai. Ending in a dot, a statement should sit atop the relevant describe() or it() call.
Instead of if
, you can use when
or while
. They are synonyms.
Using variables
You can also use variables to determine whether a test gets skipped:
skip.if(variable1).or.if(variable2).
it('should wipe out half of all life in the universe', function() {
/* ... */
})
The second if
in the example above isn't necessary. The following would also work:
skip.if(variable1).or(variable2).
it('should wipe out half of all life in the universe', function() {
/* ... */
})
If a function is passed, then the function will be called and its return value determines whether the test is skipped:
function isCatDead() {
return Math.random() >= 0.5;
}
skip.if(isCatDead).
it('should send cat back in time to search for the soul stone', function() {
/* ... */
})
Defining conditions
Multiple conditions can be specified by passing an object to skip.condition()
. This object can in turn contain multiple objects, whose keys are treated as semantically meaningful tokens:
skip.condition({
browser: {
is: {
edge: isEdge,
chrome: isChrome,
firefox: isFirefox,
}
},
os: {
is: {
mac: isMac,
windows: isWindows,
linux: isLinux,
}
}
});
skip.if.os.is.mac.and.browser.is.chrome.
describe('Browser specific test', function() {
/* ... */
})
In the example above, the tokens browser
and is
don't really do anything. They are just there to make the code read like normal English.
You can also define conditions by passing a string
and a boolean
or a function
to skip.condition()
:
skip.condition('browser.is.edge', isEdge);
skip.condition('browser.is.chrome', isChrome);
skip.condition('browser.is.firefox', isFirefox);
Starting from v1.0.2, you can do this:
skip
.condition('browser.is.edge', isEdge)
.condition('browser.is.chrome', isChrome)
.condition('browser.is.firefox', isFirefox);
Conditions can be redefined. In the examples above, browser
is not a condition. It's just a word you have to specify for semantic reason. The expression skip.if.browser.it()
would cause an error. We can make skip.if.browser
available as a check for whether the environment is a generic web-browser by redefining it:
skip.condition({
browser: {
is: {
edge: isEdge,
chrome: isChrome,
firefox: isFirefox,
}
},
os: {
is: {
mac: isMac,
windows: isWindows,
linux: isLinux,
}
}
});
skip.condition('browser', isBrowser);
Now you can use both skip.if.browser
and skip.if.browser.is.chrome
.
Note that the module will scan function
objects for attached properties. You can therefore do the following:
function edge() {
/* ... */
}
function chrome() {
/* ... */
}
function firefox() {
/* ... */
}
function browser() {
/* ... */
}
browser.is = { edge, chrome, firefox };
skip.condition({ browser });
skip.if.browser.is.firefox.
it ('should halt and catch fire', function() {
/* ... */
})
Skipping tests permanently
This module will normally call it.skip()
or describe.skip()
when the condition specified evaluates to true. This means the test will be marked by Mocha as pending. If the test in question will never ever pass and should be skipped permanently, you can accomplish that by adding forever
to the expression:
skip.forever.if.browser.is.ie.
describe('Browser specific test', function() {
/* this will never succeed in IE */
})
The synonyms entirely
and permanently
can be used in place of forever
.
Inverting conditions
Normally, a test is skipped when the condition specified is true. You can invert the behavior--skipping a test when the condition is false--by using unless
instead of if
:
skip.unless.os.is.mac.and.browser.is.chrome.
describe('Browser specific test', function() {
/* ... */
})
The synonym until
can also be used.
Extra care needs to be taken when using unless
to check for existence of a function as the function could end up being called instead. The following, for instance, does not work:
skip.unless(global.gc).
it('should not leak memory', function() {
/* ... */
})
The test will be skipped even when Node is started with the command-line option --expose-gc
, since gc()
returns undefined
. You need to do this instead:
skip.unless(!!global.gc).
it('should not leak memory', function() {
/* ... */
})
or
skip.if(!global.gc).
it('should not leak memory', function() {
/* ... */
})
Parametric conditions
When a function with named arguments is given as a condition, then it has to be invoked with arguments. The following would throw an error for instance:
skip.condition('browser', (name) => {
/* ... */
});
skip.if.browser.
it('should test something', function() {
/* ... */
})
You can make a condition both generic and possibly more specific with the help of default argument:
skip.condition('browser', (name = 'any') => {
if (name === 'any') {
/* return true as long as we're in any browser */
} else {
/* return true if the browser is the one specified */
}
});
Now skip.if.browser.it(...)
will no longer throw. Instead, it'll invoke the callback with the default argument. The expression skip.if.browser('edge').it(...)
would cause the callback to be invoked twice: once with name
set to the default and a second time with the name given as an argument.
Callbacks can accept at most two arguments currently.
Asynchronous conditions
Sometimes, test cases would only pass if external, remote resources are available. Checking for their availability would generally require asynchronous function calls, however. This module is not designed to deal with this situation. Consider using deasync if you're faced with this problem.
Skipping a test unconditionally
Calling skip.it()
or skip.describe()
would always skip a test (or a set of tests):
skip.
it('should do the impossible', function() {
/* ... */
})
Creating isolated instances
Most of the time, test conditions affect your entire test suite and the use of the global skip
object is sufficient. When conditions are localized to a particular test script, you might find it useful to use an isolated instance of Skip. You create one by calling skip
as a function.
const skip = global.skip({
/* conditions */
});