assert
is a core library that provides the basis for testing code. Node’s
assertions works pretty much like the same feature in other languages and
environments: they allow you to make claims about objects and function
calls and send out messages when the assertions are violated. These
methods are really easy to get started with and provide a great way to
unit test your code’s features. Node’s own tests are written with assert
.
Most assert
methods come in pairs: one method providing the positive test and the
other providing the negative one. For
instance, Example 5-37 shows equal()
and not
Equal()
. The methods take two arguments: the first is the expected
value, and the second is the actual value.
Example 5-37. Basic assertions
> var assert = require('assert'); > assert.equal(1, true, 'Truthy'); > assert.notEqual(1, true, 'Truthy'); AssertionError: Truthy at [object Context]:1:8 at Interface.<anonymous> (repl.js:171:22) at Interface.emit (events.js:64:17) at Interface._onLine (readline.js:153:10) at Interface._line (readline.js:408:8) at Interface._ttyWrite (readline.js:585:14) at ReadStream.<anonymous> (readline.js:73:12) at ReadStream.emit (events.js:81:20) at ReadStream._emitKey (tty_posix.js:307:10) at ReadStream.onData (tty_posix.js:70:12) >
The most obvious thing here is that when an
assert
method doesn’t pass, it throws
an exception. This is a fundamental principle in the test suites. When a
test suite runs, it should just run, without throwing an exception. If
that is the case, the test is successful.
There are just a few assertions. equal()
and notEqual()
check for the ==
equality
and !=
inequality
operators. This means they test weakly for truthy and
falsy values, as Crockford termed them. In brief,
when tested as a Boolean, falsy values consist of false
, 0, empty strings (i.e., ""
), null
,
undefined
, and NaN
. All other values are truthy. A string such
as "false"
is truthy. A string
containing "0"
is also truthy. As such,
equal()
and notEqual()
are fine to compare simple values
(strings, numbers, etc.) with each other, but you should be careful
checking against Booleans to ensure you got the result you wanted.
The stringEqual()
and notStrictEqual()
methods test equality with ===
and !==
, which will ensure that only
actual values of true
and false
are treated as true and false,
respectively. The ok()
method, shown in
Example 5-38, is a shorthand for testing whether
something is truthy, by comparing the value with true
using ==
.
Example 5-38. Testing whether something is truthy with assert.ok( )
> assert.ok('This is a string', 'Strings that are not empty are truthy'); > assert.ok(0, 'Zero is not truthy'); AssertionError: Zero is not truthy at [object Context]:1:8 at Interface.<anonymous> (repl.js:171:22) at Interface.emit (events.js:64:17) at Interface._onLine (readline.js:153:10) at Interface._line (readline.js:408:8) at Interface._ttyWrite (readline.js:585:14) at ReadStream.<anonymous> (readline.js:73:12) at ReadStream.emit (events.js:81:20) at ReadStream._emitKey (tty_posix.js:307:10) at ReadStream.onData (tty_posix.js:70:12) >
Often the things you want to compare aren’t
simple values, but objects. JavaScript doesn’t have a way to let objects
define equality operators on themselves, and even if it did, people often wouldn’t define the operators.
So the deepEqual()
and
notDeep
Equal()
methods provide a
way of deeply comparing object values. Without going into too many of the
gory details, these methods perform a few checks. If any check fails, the
test throws an exception. The first test checks whether the values simply
match with the ===
operator. Next, the
values are checked to see whether they are Buffer
s and, if so, they are checked for their
length, and then checked byte by byte. Next, if the object types don’t
match with the ==
operator, they can’t
be equal. Finally, if the arguments are objects, more extensive tests are
done, comparing the prototypes of the two objects and the number of
properties, and then recursively performing deepEqual()
on each property.
The important point here is that deepEqual()
and notDeepEqual()
are extremely helpful and
thorough, but also potentially expensive. You should try to use them only
when needed. Although these methods will attempt to do the most efficient
tests first, it can still take a bit longer to find an inequality. If you
can provide a more specific reference, such as the property of an object
rather than the whole object, you can significantly improve the
performance of your tests.
The next assert
methods are throws()
and doesNotThrow()
. These check whether a particular block of code does or
doesn’t throw an exception. You can check for a specific exception or just
whether any exception is thrown. The methods are pretty straightforward,
but have a few options that are worth reviewing.
It might be easy to overlook these tests, but handling exceptions is an essential part of writing robust JavaScript code, so you should use the tests to make sure the code you write throws exceptions in all the correct places. Chapter 3 offers more information on how to deal with exceptions well.
To pass blocks of code to throws()
and doesNotThrow()
, wrap them in functions that take
no arguments (see Example 5-39). The exception
being tested for is optional. If one isn’t passed, throws()
will just check whether any exception
happened, and doesNotThrow()
will
ensure that an exception hasn’t been thrown. If a specific error is
passed, throws()
will check that the
specified exception and only that exception was thrown. If any other
exceptions are thrown or the exception isn’t thrown, the test will not
pass. For doesNotThrow()
, when an error
is specified, it will continue without error if any exception other than
the one specified in the argument is thrown. If an exception matching the
specified error is thrown, it will cause the test to fail.
Example 5-39. Using assert.throws( ) and assert.doesNotThrow( ) to check for exception handling
> assert.throws( ... function() { ... throw new Error("Seven Fingers. Ten is too mainstream."); ... }); > assert.doesNotThrow( ... function() { ... throw new Error("I lived in the ocean way before Nemo"); ... }); AssertionError: "Got unwanted exception (Error).." at Object._throws (assert.js:281:5) at Object.doesNotThrow (assert.js:299:11) at [object Context]:1:8 at Interface.<anonymous> (repl.js:171:22) at Interface.emit (events.js:64:17) at Interface._onLine (readline.js:153:10) at Interface._line (readline.js:408:8) at Interface._ttyWrite (readline.js:585:14) at ReadStream.<anonymous> (readline.js:73:12) at ReadStream.emit (events.js:81:20) >
There are four ways to specify the type of error to look for or avoid. Pass one of the following:
- Comparison function
The function should take the exception error as its single argument. In the function, compare the exception actually thrown to the one you expect to find out whether there is a match. Return
true
if there is a match andfalse
otherwise.- Regular expression
The library will compare the regex to the error message to find a match using the
regex.test()
method in JavaScript.- String
The library will directly compare the string to the error message.
- Object constructor
The library will perform a
typeof
test on the exception. If this test throws an error with thetypeof
constructor, then the exception matches. This can be used to makethrows()
anddoesNotThrow()
very flexible.
Get Node: Up and Running now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.