unit.js
Version:
Simple, intuitive and flexible unit testing framework for javascript / Node.js (browser and server). Integrates awesome assertions libraries like Must.js, Should.js, Assert of Node.js, Sinon.js and other friendly features (promise, IoC, plugins, ...).
1,091 lines (764 loc) • 22.7 kB
JavaScript
/**
* This file is part of the Unit.js testing framework.
*
* (c) Nicolas Tallefourtane <dev@nicolab.net>
*
* For the full copyright and license information, please view
* the LICENSE file distributed with this source code
* or visit http://unitjs.com.
*
* @author Nicolas Tallefourtane <dev@nicolab.net>
*/
'use strict';
var _ = require('lodash');
var assert = require('assert');
var should = require('should/should.js');
var must = require('must');
var stats = require('./helpers').stats;
var fail = require('./helpers').fail;
/**
* Count an assertion
* @param {string} assertion Assertion name
* @param {truthy} total Truthy to count in total
*/
function countAssertion(assertion, total) {
if (typeof stats.assertions[assertion] == 'undefined') {
stats.assertions[assertion] = 0;
}
stats.assertions[assertion]++;
if (total) {
stats.total.assertions++;
}
}
// Expose all assertions
module.exports = function (actual) {
// All assertions
return {
///////////
// Match //
///////////
is: function (expected) {
countAssertion('is', true);
must(actual).eql(expected);
return this;
},
isNot: function (expected) {
countAssertion('isNot', true);
must(actual).not.eql(expected);
return this;
},
isEqualTo: function (expected) {
countAssertion('isEqualTo', true);
assert.equal(
actual,
expected,
'(actual == expected), actual ' + typeof actual + ' is not equal to ' +
'expected ' + typeof expected
);
return this;
},
isNotEqualTo: function (expected) {
countAssertion('isNotEqualTo', true);
assert.notEqual(
actual,
expected,
'(actual != expected), actual ' + typeof actual + ' is equal to ' +
'expected' + typeof expected
);
return this;
},
isStrictEqualTo: function (expected) {
countAssertion('isStrictEqualTo', true);
assert.strictEqual(
actual,
expected,
'(actual === expected), actual ' + typeof actual + ' is not identical ' +
'to expected ' + typeof expected
);
return this;
},
isNotStrictEqualTo: function (expected) {
countAssertion('isNotStrictEqualTo', true);
assert.notStrictEqual(
actual,
expected,
'(actual !== expected), actual ' + typeof actual + ' is identical to ' +
'expected ' + typeof expected
);
return this;
},
isIdenticalTo: function (expected) {
countAssertion('isIdenticalTo');
return this.isStrictEqualTo(expected);
},
isNotIdenticalTo: function (expected) {
countAssertion('isNotIdenticalTo');
return this.isNotStrictEqualTo(expected);
},
match: function (expected) {
countAssertion('match', true);
if (typeof expected == 'function') {
if (expected(actual) !== true) {
fail('actual value tested must match.');
}
return this;
}
must(actual).match(expected);
return this;
},
notMatch: function (expected) {
countAssertion('notMatch', true);
if (typeof expected == 'function') {
if (expected(actual)) {
fail(
'actual value tested (' + actual + ') must not match (' + expected + ')'
);
}
return this;
}
must(actual).not.match(expected);
return this;
},
matchEach: function (expected) {
countAssertion('matchEach', true);
if (typeof expected == 'function') {
if (typeof actual == 'object') {
for (var k in actual) {
if (expected(actual[k], k) !== true) {
fail('matchEach()', actual[k], expected);
}
}
} else {
if (expected(actual) !== true) {
fail('matchEach()', actual, expected);
}
}
return this;
}
for (var i in expected) {
if (typeof expected[i] == 'function') {
if (typeof actual == 'object') {
for (var k in actual) {
if (expected[i](actual[k], k) !== true) {
fail('matchEach()', actual[k], expected[i]);
}
}
continue;
}
if (expected[i](actual) !== true) {
fail('matchEach()', actual, expected[i]);
}
continue;
}
this.match(expected[i]);
}
return this;
},
notMatchEach: function (expected) {
countAssertion('notMatchEach', true);
if (typeof expected == 'function') {
if (typeof actual == 'object') {
for (var k in actual) {
if (expected(actual[k], k)) {
fail('notMatchEach()', actual[k], expected);
}
}
} else {
if (expected(actual)) {
fail('notMatchEach()', actual, expected);
}
}
return this;
}
for (var i in expected) {
if (typeof expected[i] == 'function') {
if (typeof actual == 'object') {
for (var k in actual) {
if (expected[i](actual[k], k)) {
fail('notMatchEach()', actual[k], expected[i]);
}
}
continue;
}
if (expected[i](actual)) {
fail('notMatchEach()', actual, expected[i]);
}
continue;
}
this.notMatch(expected[i]);
}
return this;
},
isValid: function (expected) {
countAssertion('isValid');
return this.match(expected);
},
isNotValid: function (expected) {
countAssertion('isNotValid');
return this.notMatch(expected);
},
///////////
// Types //
///////////
isType: function (expected) {
countAssertion('isType', true);
should(actual).type(expected);
return this;
},
isNotType: function (expected) {
countAssertion('isNotType', true);
should(actual).not.type(expected);
return this;
},
isObject: function () {
countAssertion('isObject', true);
must(actual).object();
return this;
},
isArray: function () {
countAssertion('isArray', true);
must(actual).array();
return this;
},
isFunction: function () {
countAssertion('isFunction', true);
must(actual).function();
return this;
},
isString: function () {
countAssertion('isString', true);
must(actual).string();
return this;
},
isNumber: function () {
countAssertion('isNumber', true);
must(actual).number();
return this;
},
isBool: function () {
countAssertion('isBool');
return this.isBoolean();
},
isBoolean: function () {
countAssertion('isBoolean', true);
must(actual).boolean();
return this;
},
isNull: function () {
countAssertion('isNull', true);
must(actual).null();
return this;
},
isUndefined: function () {
countAssertion('isUndefined', true);
must(actual).undefined();
return this;
},
/////////////////////
// Types augmented //
/////////////////////
isRegExp: function () {
countAssertion('isRegExp', true);
must(actual).regexp();
return this;
},
isNotRegExp: function () {
countAssertion('isNotRegExp', true);
must(actual).not.regexp();
return this;
},
isDate: function () {
countAssertion('isDate', true);
must(actual).date();
return this;
},
isNotDate: function () {
countAssertion('isNotDate', true);
must(actual).not.date();
return this;
},
isArguments: function () {
countAssertion('isArguments', true);
should(actual).be.Arguments();
return this;
},
isNotArguments: function () {
countAssertion('isNotArguments', true);
should(actual).not.be.Arguments();
return this;
},
isTrue: function () {
countAssertion('isTrue', true);
must(actual).true();
return this;
},
isNotTrue: function () {
countAssertion('isNotTrue', true);
must(actual).not.true();
return this;
},
isTruthy: function () {
countAssertion('isTruthy', true);
must(actual).truthy();
return this;
},
isNotTruthy: function () {
countAssertion('isNotTruthy', true);
must(actual).not.truthy();
return this;
},
isFalse: function () {
countAssertion('isFalse', true);
must(actual).false();
return this;
},
isNotFalse: function () {
countAssertion('isNotFalse', true);
must(actual).not.false();
return this;
},
isFalsy: function () {
countAssertion('isFalsy', true);
must(actual).falsy();
return this;
},
isNotFalsy: function () {
countAssertion('isNotFalsy', true);
must(actual).not.falsy();
return this;
},
isEmpty: function () {
countAssertion('isEmpty', true);
must(actual).empty();
return this;
},
isNotEmpty: function () {
countAssertion('isNotEmpty', true);
must(actual).not.empty();
return this;
},
isNaN: function () {
countAssertion('isNaN', true);
if (!_.isNaN(actual)) {
fail('Actual value tested (' + actual + ') must be NaN');
}
return this;
},
isNotNaN: function () {
countAssertion('isNotNaN', true);
if (_.isNaN(actual)) {
fail('Actual value tested (' + actual + ') must be not NaN');
}
return this;
},
exists: function () {
countAssertion('exists', true);
must(actual).exist();
return this;
},
isError: function () {
countAssertion('isError');
return this.throws(Error);
},
throws: function (constructor, expected) {
countAssertion('throws', true);
if (arguments.length > 2) {
throw new Error('throws() assertion, takes maximum 2 arguments ' +
'(constructor and expected).');
}
if (arguments.length == 1) {
if (typeof constructor == 'function') {
assert.throws(actual, constructor);
} else {
must(actual).throw(constructor);
}
}
else if (arguments.length == 2) {
must(actual).throw(constructor, expected);
}
else {
must(actual).throw();
}
return this;
},
////////////////////
// Quantification //
////////////////////
hasLength: function (expected) {
countAssertion('hasLength', true);
if (typeof actual == 'object') {
var count = 0;
for (var k in actual) {
count++;
}
if (count !== expected) {
fail(actual + 'must has length of ' + expected);
}
} else {
must(actual).length(expected);
}
return this;
},
hasNotLength: function (expected) {
countAssertion('hasNotLength', true);
if (typeof actual == 'object') {
var count = 0;
for (var k in actual) {
count++;
}
if (count === expected) {
fail(actual + 'must has not length of ' + expected);
}
} else {
must(actual).not.length(expected);
}
return this;
},
isBetween: function (begin, end) {
countAssertion('isBetween', true);
must(actual).between(begin, end);
return this;
},
isNotBetween: function (begin, end) {
countAssertion('isNotBetween', true);
must(actual).not.between(begin, end);
return this;
},
isBefore: function (expected) {
countAssertion('isBefore', true);
must(actual).before(expected);
return this;
},
isAfter: function (expected) {
countAssertion('isAfter', true);
must(actual).after(expected);
return this;
},
isGreaterThan: function (expected) {
countAssertion('isGreaterThan', true);
must(actual).above(expected);
return this;
},
isLessThan: function (expected) {
countAssertion('isLessThan', true);
must(actual).below(expected);
return this;
},
isApprox: function (num, delta) {
countAssertion('isApprox', true);
should(actual).be.approximately(num, delta);
return this;
},
isInfinite: function () {
countAssertion('isInfinite', true);
should(actual).be.Infinity();
return this;
},
isNotInfinite: function () {
countAssertion('isNotInfinite', true);
should(actual).not.be.Infinity();
return this;
},
///////////////
// Containers //
///////////////
isEnumerable: function (property) {
countAssertion('isEnumerable', true);
must(actual).enumerable(property);
return this;
},
isNotEnumerable: function (property) {
countAssertion('isNotEnumerable', true);
must(actual).nonenumerable(property);
return this;
},
isFrozen: function () {
countAssertion('isFrozen', true);
must(actual).frozen();
return this;
},
isNotFrozen: function () {
countAssertion('isNotFrozen', true);
must(actual).not.frozen();
return this;
},
isInstanceOf: function (expected) {
countAssertion('isInstanceOf', true);
must(actual).instanceof(expected);
return this;
},
isNotInstanceOf: function (expected) {
countAssertion('isNotInstanceOf', true);
must(actual).not.instanceof(expected);
return this;
},
hasProperty: function (property, value) {
countAssertion('hasProperty', true);
if (!arguments.length) {
throw new Error('hasProperty() asserter require the argument ' +
'"property" (the name of the property).');
}
else if (arguments.length == 1) {
must(actual).property(property);
}
else if (arguments.length == 2) {
must(actual).property(property, value);
}
else {
throw new Error('hasProperty() asserter, takes maximum 2 arguments ' +
'(property and value).');
}
return this;
},
hasNotProperty: function (property, value) {
countAssertion('hasNotProperty', true);
if (arguments.length === 0) {
throw new Error('hasNotProperty() asserter require the argument ' +
'"property" (the name of the property).');
}
else if (arguments.length === 1) {
must(actual).not.property(property);
}
else if (arguments.length === 2) {
must(actual).not.property(property, value);
}
else {
throw new Error('hasNotProperty() asserter, takes maximum ' +
'2 arguments (property and value).');
}
return this;
},
hasOwnProperty: function (property, value) {
countAssertion('hasOwnProperty ', true);
if (arguments.length === 0) {
throw new Error('hasOwnProperty() asserter require the argument ' +
'"property" (the name of the property).');
}
else if (arguments.length === 1) {
must(actual).ownProperty(property);
}
else if (arguments.length === 2) {
must(actual).ownProperty(property, value);
}
else {
throw new Error('hasOwnProperty() asserter, takes maximum ' +
'2 arguments (property and value).');
}
return this;
},
hasNotOwnProperty: function (property, value) {
countAssertion('hasNotOwnProperty', true);
if (arguments.length === 0) {
throw new Error('hasNotOwnProperty() asserter require the argument ' +
'"property" (the name of the property).');
}
else if (arguments.length === 1) {
must(actual).not.ownProperty(property);
}
else if (arguments.length === 2) {
must(actual).not.ownProperty(property, value);
}
else {
throw new Error('hasNotOwnProperty() asserter, takes maximum ' +
'2 arguments (property and value).');
}
return this;
},
hasProperties: function (properties) {
countAssertion('hasProperties', true);
must(actual).keys(properties);
return this;
},
hasNotProperties: function (properties) {
countAssertion('hasNotProperties', true);
must(actual).not.keys(properties);
return this;
},
hasOwnProperties: function (properties) {
countAssertion('hasOwnProperties', true);
must(actual).ownKeys(properties);
return this;
},
hasKey: function (key, value) {
countAssertion('hasKey');
return this.hasProperty.apply(this, arguments);
},
notHasKey: function (key, value) {
countAssertion('notHasKey');
return this.hasNotProperty.apply(this, arguments);
},
hasKeys: function (keys) {
countAssertion('hasKeys');
return this.hasProperties(keys);
},
notHasKeys: function (keys) {
countAssertion('notHasKeys');
return this.hasNotProperties(keys);
},
hasValue: function (expected) {
countAssertion('hasValue', true);
must(actual).include(expected);
return this;
},
notHasValue: function (expected) {
countAssertion('notHasValue', true);
must(actual).not.include(expected);
return this;
},
hasValues: function (expected) {
countAssertion('hasValues', true);
for (var k in expected) {
must(actual).include(expected[k]);
}
return this;
},
notHasValues: function (expected) {
countAssertion('notHasValues', true);
for (var k in expected) {
must(actual).not.include(expected[k]);
}
return this;
},
contains: function (expected) {
countAssertion('contains', true);
if (arguments.length > 1) {
for (var k in arguments) {
if (typeof arguments[k] === 'object') {
should(actual).containDeep(arguments[k]);
} else {
must(actual).contain(arguments[k]);
}
}
}
else if (typeof actual === 'object') {
should(actual).containDeep(expected);
}
else {
must(actual).contain(expected);
}
return this;
},
notContains: function (expected) {
countAssertion('notContains', true);
if (arguments.length > 1) {
for (var k in arguments) {
if (typeof arguments[k] === 'object') {
should(actual).not.containDeep(arguments[k]);
} else {
must(actual).not.contain(arguments[k]);
}
}
}
else if (typeof actual === 'object') {
should(actual).not.containDeep(expected);
}
else {
must(actual).not.contain(expected);
}
return this;
},
isReverseOf: function (expected) {
countAssertion('isReverseOf');
this.is(expected.reverse());
must(actual).permutationOf(expected);
return this;
},
isNotReverseOf: function (expected) {
countAssertion('isNotReverseOf');
this.isNot(expected.reverse());
must(actual).not.permutationOf(expected);
return this;
},
////////////
// String //
////////////
startsWith: function (str) {
countAssertion('startsWith', true);
should(actual).startWith(str);
return this;
},
notStartsWith: function (str) {
countAssertion('notStartsWith', true);
should(actual).not.startWith(str);
return this;
},
endsWith: function (str) {
countAssertion('endsWith', true);
should(actual).endWith(str);
return this;
},
notEndsWith: function (str) {
countAssertion('notEndsWith', true);
should(actual).not.endWith(str);
return this;
},
///////////////////
// HTTP headers //
///////////////////
hasHttpStatus: function (code) {
countAssertion('hasHttpStatus', true);
must(actual).property('statusCode', code);
return this;
},
notHasHttpStatus: function (code) {
countAssertion('notHasHttpStatus', true);
must(actual).not.property('statusCode', code);
return this;
},
hasHeader: function (field, value) {
countAssertion('hasHeader', true);
if (arguments.length === 0 || arguments.length > 2) {
throw new Error('hasHeader() asserter, takes minimum the "field" ' +
'argument, maximum 2 arguments (field and value).');
}
must(actual).property('headers');
if (arguments.length === 1) {
must(actual.headers).property(field.toLowerCase());
} else {
must(actual.headers).property(field.toLowerCase(), value);
}
return this;
},
notHasHeader: function (field, value) {
countAssertion('notHasHeader', true);
if (arguments.length === 0 || arguments.length > 2) {
throw new Error('notHasHeader() asserter, takes minimum the "field" ' +
'argument, maximum 2 arguments (field and value).');
}
if (arguments.length === 1) {
must(actual.headers).not.property(field.toLowerCase());
} else {
must(actual.headers).not.property(field.toLowerCase(), value);
}
return this;
},
hasHeaderJson: function () {
countAssertion('hasHeaderJson', true);
must(actual).property('headers');
must(actual.headers).property('content-type');
must(actual.headers['content-type']).match(/application\/json/i);
return this;
},
notHasHeaderJson: function () {
countAssertion('notHasHeaderJson', true);
if (actual && actual.headers && 'content-type' in actual.headers) {
must(actual.headers['content-type']).not.match(/application\/json/i);
}
return this;
},
hasHeaderHtml: function () {
countAssertion('hasHeaderHtml', true);
must(actual).property('headers');
must(actual.headers).property('content-type');
must(actual.headers['content-type']).match(/text\/html/i);
return this;
},
notHasHeaderHtml: function () {
countAssertion('notHasHeaderHtml', true);
if (actual && actual.headers && 'content-type' in actual.headers) {
must(actual.headers['content-type']).not.match(/text\/html/i);
}
return this;
}
};
};