Each test we wrote for capitalize()
is commonly referred to as an assert. Asserts are a vital part of testing. They're the assertions about the code functionality:
import capitalize from '../src/capitalize.js';
// First statement (check for an empty string)
if (capitalize('') !== '') {
throw new Error('The function is not working properly!');
}
// Second statement (word check)
if (capitalize('hello') !== 'Hello') {
throw new Error('The function is not working properly!');
}
You may have noticed that all checks have the same structure: condition => exception. Node.js comes with an assert module that has several functions to make it easier to write asserts:
// This unusual import is due to the fact that
// that assert exported by default is considered obsolete
// It's better to use strict
import { strict as assert } from 'node:assert';
import capitalize from '../src/capitalize.js';
// The check has changed from a negative check to a positive check
assert(capitalize('') === '');
assert(capitalize('hello') === 'Hello');
At its simplest, assert is used as a function that checks the truth of a value passed to a function. In other words, assert(true)
means all is well, and assert(false)
indicates an error. The last option throws an exception with this message:
AssertionError [ERR_ASSERTION]: false == true
It basically means, "the value of the expression was expected to be true, but it turned out to be false". In addition to the message, it also displays a backtrace to specify the statement that triggered the message:
// In this case, the assert was triggered on line 15 of the capitalize.js file
at first (file:///src/capitalize.js:15:19)
at default (file:///src/capitalize.js:11:3)
at file:///test.js:5:13
The assert()
made our code shorter and easier to understand. A positive check looks more natural because it's what we expect.
On the other hand, the error message is extremely uninformative. The only way to figure out what happened is to open the code with the dodgy statement (there's also an option to pass the error message with the last parameter, but this is rarely done because it requires too much effort). You can enhance this using specialized statements designed for particular cases. For example, when comparing two values, the assert.strictEqual(actual, expected)
function is a good choice. Let's rewrite the code above:
import { strict as assert } from 'node:assert';
// when using strict mode
// the equal check is equal to strictEqual
import capitalize from '../src/capitalize.js';
// The check has changed from a negative check to a positive one
assert.equal(capitalize(''), '');
// The first parameter, actual, is what we got
// The second parameter, expected, is what the test expects
// Putting parameters in the correct order is extremely important when it comes to analyzing errors
assert.equal(capitalize('hello'), 'Hello');
The output from these assertions is much clearer now:
Thrown:
AssertionError [ERR_ASSERTION]: 'hello' == 'Hello'
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'hello',
expected: 'Hello',
operator: '=='
https://repl.it/@hexlet/js-testing-asserts-capitalize-en#tests/capitalize.test.js
This output contains not only information about the error, but also the data that was passed to the assertion. This format simplifies problem analysis and speeds up debugging.
However, you should be careful. The strictEqual(actual, expected)
function performs equality check by reference. I.e., two different objects with the same content will not be treated as though they are equivalent:
AssertionError [ERR_ASSERTION]: Values have same structure but are not reference-equal:
{
key: 'value'
}
at repl:1:8
at Script.runInThisContext (vm.js:131:20)
at REPLServer.defaultEval (repl.js:436:29)
at bound (domain.js:429:14)
at REPLServer.runBound [as eval] (domain.js:442:12)
at REPLServer.onLine (repl.js:763:10)
at REPLServer.emit (events.js:327:22)
at REPLServer.EventEmitter.emit (domain.js:485:12)
at REPLServer.Interface._onLine (readline.js:337:10)
at REPLServer.Interface._line (readline.js:666:8) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [Object],
expected: [Object],
operator: 'strictEqual'
}
There's a different assertion we use to compare values: assert.deepEqual(actual, expected)
. It relies only on the content:
assert.deepEqual({}, {}); // all good
assert.deepEqual({ key: 'value' }, { key: 'value' }); // all good
assert.deepEqual({ key: 'value' }, { key: 'another value' }); // Boom!
Actually, the rules for checking using function are quite complicated. You can read more about it in the documentation
The functions assert.notStrictEqual(actual, expected)
and assert.notDeepStrictEqual(actual, expected)
are designed to test negative scenarios. They test to see if the values are NOT equal, not to see if they ARE equal. These assertions are rarely used, but it's still useful to know about them:
assert.notDeepEqual({ a: 1 }, { a: '1' }); // OK!
https://repl.it/@hexlet/js-testing-asserts-methods-en#index.test.js
Do it yourself
- Check out the links to documentation on assertion libraries at the end of the lesson
- Replace the manual assertions in your repository with the assert module
- Run the tests, make sure they work. Try to fail them
- Add the code to the GitHub
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.