The BDD style is exposed through expect
or should
interfaces. In both
scenarios, you chain together natural language assertions.
// expect
var expect = require('chai').expect;
expect(foo).to.equal('bar');
// should
var should = require('chai').should();
foo.should.equal('bar');
Differences
The expect
interface provides a function as a starting point for chaining
your language assertions. It works on node.js and in all browsers.
The should
interface extends Object.prototype
to provide a single getter as
the starting point for your language assertions. It works on node.js and in
all browsers except Internet Explorer.
Configuration
By default, Chai does not show stack traces upon an AssertionError. This can
be changed by modifying the includeStack
parameter for chai.Assertion. For example:
var chai = require('chai');
chai.Assertion.includeStack = true; // defaults to false
Language chain. Also tests tense
to past for addon
modules that use the tense feature.
View Source
Object.defineProperty(Assertion.prototype, 'been',
{ get: function () {
flag(this, 'tense', 'past');
return this;
}
, configurable: true
});
an()
Assert typeof. Also can be used as a language chain.
expect('test').to.be.a('string');
expect(foo).to.be.an.instanceof(Foo);
View Source
var an = function () {
var assert = function(type) {
var obj = flag(this, 'object')
, klass = type.charAt(0).toUpperCase() + type.slice(1);
this.assert(
'[object ' + klass + ']' === toString.call(obj)
, 'expected #{this} to be a ' + type
, 'expected #{this} not to be a ' + type
, '[object ' + klass + ']'
, toString.call(obj)
);
return this;
};
assert.__proto__ = this;
return assert;
};
Object.defineProperty(Assertion.prototype, 'an',
{ get: an
, configurable: true
});
Object.defineProperty(Assertion.prototype, 'a',
{ get: an
, configurable: true
});
include()
Assert the inclusion of an object in an Array or substring in string.
Also toggles the contain
flag for the keys
assertion if used as property.
expect([1,2,3]).to.include(2);
View Source
var include = function () {
flag(this, 'contains', true);
var assert = function(val) {
var obj = flag(this, 'object')
this.assert(
~obj.indexOf(val)
, 'expected #{this} to include ' + util.inspect(val)
, 'expected #{this} to not include ' + util.inspect(val));
return this;
};
assert.__proto__ = this;
return assert;
};
Object.defineProperty(Assertion.prototype, 'contain',
{ get: include
, configurable: true
});
Object.defineProperty(Assertion.prototype, 'include',
{ get: include
, configurable: true
});
Negates any of assertions following in the chain.
View Source
Object.defineProperty(Assertion.prototype, 'not',
{ get: function () {
flag(this, 'negate', true);
return this;
}
, configurable: true
});
Assert object truthiness.
expect('everthing').to.be.ok;
expect(false).to.not.be.ok;
expect(undefined).to.not.be.ok;
expect(null).to.not.be.ok;
View Source
Object.defineProperty(Assertion.prototype, 'ok',
{ get: function () {
this.assert(
flag(this, 'object')
, 'expected #{this} to be truthy'
, 'expected #{this} to be falsy');
return this;
}
, configurable: true
});
View Source
Object.defineProperty(Assertion.prototype, 'true',
{ get: function () {
this.assert(
true === flag(this, 'object')
, 'expected #{this} to be true'
, 'expected #{this} to be false'
, this.negate ? false : true
);
return this;
}
, configurable: true
});
View Source
Object.defineProperty(Assertion.prototype, 'false',
{ get: function () {
this.assert(
false === flag(this, 'object')
, 'expected #{this} to be false'
, 'expected #{this} to be true'
, this.negate ? true : false
);
return this;
}
, configurable: true
});
Assert object exists (null).
var foo = 'hi'
, bar;
expect(foo).to.exist;
expect(bar).to.not.exist;
View Source
Object.defineProperty(Assertion.prototype, 'exist',
{ get: function () {
this.assert(
null != flag(this, 'object')
, 'expected #{this} to exist'
, 'expected #{this} to not exist'
);
return this;
}
, configurable: true
});
Assert object's length to be 0.
expect([]).to.be.empty;
View Source
Object.defineProperty(Assertion.prototype, 'empty',
{ get: function () {
var obj = flag(this, 'object')
, expected = obj;
if (Array.isArray(obj)) {
expected = obj.length;
} else if (typeof obj === 'object') {
expected = Object.keys(obj).length;
}
this.assert(
!expected
, 'expected #{this} to be empty'
, 'expected #{this} not to be empty');
return this;
}
, configurable: true
});
Assert object is an instanceof arguments.
function test () {
expect(arguments).to.be.arguments;
}
View Source
Object.defineProperty(Assertion.prototype, 'arguments',
{ get: function () {
var obj = flag(this, 'object');
this.assert(
'[object Arguments]' == Object.prototype.toString.call(obj)
, 'expected #{this} to be arguments'
, 'expected #{this} to not be arguments'
, '[object Arguments]'
, Object.prototype.toString.call(obj)
);
return this;
}
, configurable: true
});
Assertion.prototype.equal()
Assert strict equality.
expect('hello').to.equal('hello');
View Source
Assertion.prototype.equal = function (val) {
this.assert(
val === flag(this, 'object')
, 'expected #{this} to equal #{exp}'
, 'expected #{this} to not equal #{exp}'
, val );
return this;
};
Assertion.prototype.eql()
Assert deep equality.
expect({ foo: 'bar' }).to.eql({ foo: 'bar' });
View Source
Assertion.prototype.eql = function (obj) {
this.assert(
util.eql(obj, flag(this, 'object'))
, 'expected #{this} to equal #{exp}'
, 'expected #{this} to not equal #{exp}'
, obj );
return this;
};
Assertion.prototype.above()
Assert greater than value
.
expect(10).to.be.above(5);
View Source
Assertion.prototype.above = function (val) {
this.assert(
flag(this, 'object') > val
, 'expected #{this} to be above ' + val
, 'expected #{this} to be below ' + val);
return this;
};
Assertion.prototype.below()
Assert less than value
.
expect(5).to.be.below(10);
View Source
Assertion.prototype.below = function (val) {
this.assert(
flag(this, 'object') < val
, 'expected #{this} to be below ' + val
, 'expected #{this} to be above ' + val);
return this;
};
Assertion.prototype.within()
Assert that a number is within a range.
expect(7).to.be.within(5,10);
View Source
Assertion.prototype.within = function (start, finish) {
var obj = flag(this, 'object')
, range = start + '..' + finish;
this.assert(
obj >= start && obj <= finish
, 'expected #{this} to be within ' + range
, 'expected #{this} to not be within ' + range);
return this;
};
Assertion.prototype.instanceOf()
Assert instanceof.
var Tea = function (name) { this.name = name; }
, Chai = new Tea('chai');
expect(Chai).to.be.an.instanceof(Tea);
View Source
Assertion.prototype.instanceOf = function (constructor) {
var name = util.getName(constructor);
this.assert(
flag(this, 'object') instanceof constructor
, 'expected #{this} to be an instance of ' + name
, 'expected #{this} to not be an instance of ' + name);
return this;
};
Assertion.prototype.property()
Assert that property of name
exists, optionally with value
.
var obj = { foo: 'bar' }
expect(obj).to.have.property('foo');
expect(obj).to.have.property('foo', 'bar');
expect(obj).to.have.property('foo').to.be.a('string');
View Source
Assertion.prototype.property = function (name, val) {
var obj = flag(this, 'object')
, value = util.getPathValue(name, obj)
, negate = flag(this, 'negate');
if (negate && undefined !== val) {
if (undefined === value) {
throw new Error(util.inspect(obj) + ' has no property ' + util.inspect(name));
}
} else {
this.assert(
undefined !== value
, 'expected #{this} to have a property ' + util.inspect(name)
, 'expected #{this} to not have property ' + util.inspect(name));
}
if (undefined !== val) {
this.assert(
val === value
, 'expected #{this} to have a property ' + util.inspect(name) + ' of #{exp}, but got #{act}'
, 'expected #{this} to not have a property ' + util.inspect(name) + ' of #{act}'
, val
, value
);
}
flag(this, 'object', value);
return this;
};
Assertion.prototype.ownProperty()
Assert that has own property by name
.
expect('test').to.have.ownProperty('length');
View Source
Assertion.prototype.ownProperty = function (name) {
var obj = flag(this, 'object');
this.assert(
obj.hasOwnProperty(name)
, 'expected #{this} to have own property ' + util.inspect(name)
, 'expected #{this} to not have own property ' + util.inspect(name));
return this;
};
Assertion.prototype.length()
Assert that object has expected length.
expect([1,2,3]).to.have.length(3);
expect('foobar').to.have.length(6);
View Source
Assertion.prototype.length = function (n) {
var obj = flag(this, 'object');
new Assertion(obj).to.have.property('length');
var len = obj.length;
this.assert(
len == n
, 'expected #{this} to have a length of #{exp} but got #{act}'
, 'expected #{this} to not have a length of #{act}'
, n
, len
);
return this;
};
Assertion.prototype.match()
Assert that matches regular expression.
expect('foobar').to.match(/^foo/);
View Source
Assertion.prototype.match = function (re) {
var obj = flag(this, 'object');
this.assert(
re.exec(obj)
, 'expected #{this} to match ' + re
, 'expected #{this} not to match ' + re);
return this;
};
Assertion.prototype.string()
Assert inclusion of string in string.
expect('foobar').to.have.string('bar');
View Source
Assertion.prototype.string = function (str) {
var obj = flag(this, 'object');
new Assertion(obj).is.a('string');
this.assert(
~obj.indexOf(str)
, 'expected #{this} to contain ' + util.inspect(str)
, 'expected #{this} to not contain ' + util.inspect(str));
return this;
};
Assertion.prototype.keys()
Assert exact keys or the inclusing of keys using the contain
modifier.
expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']);
expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar');
View Source
Assertion.prototype.keys = function(keys) {
var obj = flag(this, 'object')
, str
, ok = true;
keys = keys instanceof Array
? keys
: Array.prototype.slice.call(arguments);
if (!keys.length) throw new Error('keys required');
var actual = Object.keys(obj)
, len = keys.length;
// Inclusion
ok = keys.every(function(key){
return ~actual.indexOf(key);
});
// Strict
if (!flag(this, 'negate') && !flag(this, 'contains')) {
ok = ok && keys.length == actual.length;
}
// Key string
if (len > 1) {
keys = keys.map(function(key){
return util.inspect(key);
});
var last = keys.pop();
str = keys.join(', ') + ', and ' + last;
} else {
str = util.inspect(keys[0]);
}
// Form
str = (len > 1 ? 'keys ' : 'key ') + str;
// Have / include
str = (flag(this, 'contains') ? 'contain ' : 'have ') + str;
// Assertion
this.assert(
ok
, 'expected #{this} to ' + str
, 'expected #{this} to not ' + str
, keys
, Object.keys(obj)
);
return this;
}
Assertion.prototype.Throw()
Assert that a function will throw a specific type of error, or specific type of error
(as determined using instanceof
), optionally with a RegExp or string inclusion test
for the error's message.
var err = new ReferenceError('This is a bad function.');
var fn = function () { throw err; }
expect(fn).to.throw(ReferenceError);
expect(fn).to.throw(Error);
expect(fn).to.throw(/bad function/);
expect(fn).to.not.throw('good function');
expect(fn).to.throw(ReferenceError, /bad function/);
expect(fn).to.throw(err);
expect(fn).to.not.throw(new RangeError('Out of range.'));
Please note that when a throw expectation is negated, it will check each
parameter independently, starting with error constructor type. The appropriate way
to check for the existence of a type of error but for a message that does not match
is to use and
.
expect(fn).to.throw(ReferenceError).and.not.throw(/good function/);
View Source
Assertion.prototype.Throw = function (constructor, msg) {
var obj = flag(this, 'object');
new Assertion(obj).is.a('function');
var thrown = false
, desiredError = null
, name = null;
if (arguments.length === 0) {
msg = null;
constructor = null;
} else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) {
msg = constructor;
constructor = null;
} else if (constructor && constructor instanceof Error) {
desiredError = constructor;
constructor = null;
msg = null;
} else if (typeof constructor === 'function') {
name = (new constructor()).name;
} else {
constructor = null;
}
try {
obj();
} catch (err) {
// first, check desired error
if (desiredError) {
this.assert(
err === desiredError
, 'expected #{this} to throw ' + util.inspect(desiredError) + ' but ' + util.inspect(err) + ' was thrown'
, 'expected #{this} to not throw ' + util.inspect(desiredError)
);
return this;
}
// next, check constructor
if (constructor) {
this.assert(
err instanceof constructor
, 'expected #{this} to throw ' + name + ' but a ' + err.name + ' was thrown'
, 'expected #{this} to not throw ' + name );
if (!msg) return this;
}
// next, check message
if (err.message && msg && msg instanceof RegExp) {
this.assert(
msg.exec(err.message)
, 'expected #{this} to throw error matching ' + msg + ' but got ' + util.inspect(err.message)
, 'expected #{this} to throw error not matching ' + msg
);
return this;
} else if (err.message && msg && 'string' === typeof msg) {
this.assert(
~err.message.indexOf(msg)
, 'expected #{this} to throw error including #{exp} but got #{act}'
, 'expected #{this} to throw error not including #{act}'
, msg
, err.message
);
return this;
} else {
thrown = true;
}
}
var expectedThrown = name ? name : desiredError ? util.inspect(desiredError) : 'an error';
this.assert(
thrown === true
, 'expected #{this} to throw ' + expectedThrown
, 'expected #{this} to not throw ' + expectedThrown);
return this;
};
Assertion.prototype.respondTo()
Assert that object/class will respond to a method.
expect(Klass).to.respondTo('bar');
expect(obj).to.respondTo('bar');
View Source
Assertion.prototype.respondTo = function (method) {
var obj = flag(this, 'object')
, context = ('function' === typeof obj)
? obj.prototype[method]
: obj[method];
this.assert(
'function' === typeof context
, 'expected #{this} to respond to ' + util.inspect(method)
, 'expected #{this} to not respond to ' + util.inspect(method)
, 'function'
, typeof context
);
return this;
};
Assertion.prototype.satisfy()
Assert that passes a truth test.
expect(1).to.satisfy(function(num) { return num > 0; });
View Source
Assertion.prototype.satisfy = function (matcher) {
var obj = flag(this, 'object');
this.assert(
matcher(obj)
, 'expected #{this} to satisfy ' + util.inspect(matcher)
, 'expected #{this} to not satisfy' + util.inspect(matcher)
, this.negate ? false : true
, matcher(obj)
);
return this;
};
Assertion.prototype.closeTo()
Assert that actual is equal to +/- delta.
expect(1.5).to.be.closeTo(1, 0.5);
View Source
Assertion.prototype.closeTo = function (expected, delta) {
var obj = flag(this, 'object');
this.assert(
(obj - delta === expected) || (obj + delta === expected)
, 'expected #{this} to be close to ' + expected + ' +/- ' + delta
, 'expected #{this} not to be close to ' + expected + ' +/- ' + delta);
return this;
};