Skip to content

Commit fc2bbe5

Browse files
committed
assert: introduce deepStrictEqual
`deepStrictEqual` works the same way as `strictEqual`, but uses `===` to compare primitives and requires prototypes of equal objects to be the same object. Fixes: nodejs/node-v0.x-archive#7161
1 parent b9d3928 commit fc2bbe5

File tree

3 files changed

+155
-13
lines changed

3 files changed

+155
-13
lines changed

doc/api/assert.markdown

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,30 @@ Tests shallow, coercive non-equality with the not equal comparison operator ( `!
2323

2424
## assert.deepEqual(actual, expected[, message])
2525

26-
Tests for deep equality.
26+
Tests for deep equality. Primitive values are compared with the equal comparison
27+
operator ( `==` ). Doesn't take object prototypes into account.
2728

2829
## assert.notDeepEqual(actual, expected[, message])
2930

30-
Tests for any deep inequality.
31+
Tests for any deep inequality. Opposite of `assert.deepEqual`.
3132

3233
## assert.strictEqual(actual, expected[, message])
3334

3435
Tests strict equality, as determined by the strict equality operator ( `===` )
3536

3637
## assert.notStrictEqual(actual, expected[, message])
3738

38-
Tests strict non-equality, as determined by the strict not equal operator ( `!==` )
39+
Tests strict non-equality, as determined by the strict not equal
40+
operator ( `!==` )
41+
42+
## assert.deepStrictEqual(actual, expected[, message])
43+
44+
Tests for deep equality. Primitive values are compared with the strict equality
45+
operator ( `===` ).
46+
47+
## assert.notDeepStrictEqual(actual, expected[, message])
48+
49+
Tests for deep inequality. Opposite of `assert.deepStrictEqual`.
3950

4051
## assert.throws(block[, error][, message])
4152

lib/assert.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,18 @@ assert.notEqual = function notEqual(actual, expected, message) {
129129
// assert.deepEqual(actual, expected, message_opt);
130130

131131
assert.deepEqual = function deepEqual(actual, expected, message) {
132-
if (!_deepEqual(actual, expected)) {
132+
if (!_deepEqual(actual, expected, false)) {
133133
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
134134
}
135135
};
136136

137-
function _deepEqual(actual, expected) {
137+
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
138+
if (!_deepEqual(actual, expected, true)) {
139+
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
140+
}
141+
};
142+
143+
function _deepEqual(actual, expected, strict) {
138144
// 7.1. All identical values are equivalent, as determined by ===.
139145
if (actual === expected) {
140146
return true;
@@ -166,7 +172,7 @@ function _deepEqual(actual, expected) {
166172
// equivalence is determined by ==.
167173
} else if ((actual === null || typeof actual !== 'object') &&
168174
(expected === null || typeof expected !== 'object')) {
169-
return actual == expected;
175+
return strict ? actual === expected : actual == expected;
170176

171177
// 7.5 For all other Object pairs, including Array objects, equivalence is
172178
// determined by having the same number of owned properties (as verified
@@ -175,49 +181,51 @@ function _deepEqual(actual, expected) {
175181
// corresponding key, and an identical 'prototype' property. Note: this
176182
// accounts for both named and indexed properties on Arrays.
177183
} else {
178-
return objEquiv(actual, expected);
184+
return objEquiv(actual, expected, strict);
179185
}
180186
}
181187

182188
function isArguments(object) {
183189
return Object.prototype.toString.call(object) == '[object Arguments]';
184190
}
185191

186-
function objEquiv(a, b) {
192+
function objEquiv(a, b, strict) {
187193
if (a === null || a === undefined || b === null || b === undefined)
188194
return false;
189195
// if one is a primitive, the other must be same
190196
if (util.isPrimitive(a) || util.isPrimitive(b))
191197
return a === b;
198+
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
199+
return false;
192200
var aIsArgs = isArguments(a),
193201
bIsArgs = isArguments(b);
194202
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
195203
return false;
196204
if (aIsArgs) {
197205
a = pSlice.call(a);
198206
b = pSlice.call(b);
199-
return _deepEqual(a, b);
207+
return _deepEqual(a, b, strict);
200208
}
201209
var ka = Object.keys(a),
202210
kb = Object.keys(b),
203211
key, i;
204212
// having the same number of owned properties (keys incorporates
205213
// hasOwnProperty)
206-
if (ka.length != kb.length)
214+
if (ka.length !== kb.length)
207215
return false;
208216
//the same set of keys (although not necessarily the same order),
209217
ka.sort();
210218
kb.sort();
211219
//~~~cheap key test
212220
for (i = ka.length - 1; i >= 0; i--) {
213-
if (ka[i] != kb[i])
221+
if (ka[i] !== kb[i])
214222
return false;
215223
}
216224
//equivalent values for every corresponding key, and
217225
//~~~possibly expensive deep test
218226
for (i = ka.length - 1; i >= 0; i--) {
219227
key = ka[i];
220-
if (!_deepEqual(a[key], b[key])) return false;
228+
if (!_deepEqual(a[key], b[key], strict)) return false;
221229
}
222230
return true;
223231
}
@@ -226,11 +234,19 @@ function objEquiv(a, b) {
226234
// assert.notDeepEqual(actual, expected, message_opt);
227235

228236
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
229-
if (_deepEqual(actual, expected)) {
237+
if (_deepEqual(actual, expected, false)) {
230238
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
231239
}
232240
};
233241

242+
assert.notDeepStrictEqual = notDeepStrictEqual;
243+
function notDeepStrictEqual(actual, expected, message) {
244+
if (_deepEqual(actual, expected, true)) {
245+
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
246+
}
247+
}
248+
249+
234250
// 9. The strict equality assertion tests strict equality, as determined by ===.
235251
// assert.strictEqual(actual, expected, message_opt);
236252

test/parallel/test-assert.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,121 @@ assert.doesNotThrow(makeBlock(a.deepEqual, new String('a'), {0: 'a'}), a.Asserti
149149
assert.doesNotThrow(makeBlock(a.deepEqual, new Number(1), {}), a.AssertionError);
150150
assert.doesNotThrow(makeBlock(a.deepEqual, new Boolean(true), {}), a.AssertionError);
151151

152+
//deepStrictEqual
153+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, new Date(2000, 3, 14),
154+
new Date(2000, 3, 14)), 'deepStrictEqual date');
155+
156+
assert.throws(makeBlock(a.deepStrictEqual, new Date(), new Date(2000, 3, 14)),
157+
a.AssertionError,
158+
'deepStrictEqual date');
159+
160+
// 7.3 - strict
161+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/, /a/));
162+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/g, /a/g));
163+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/i, /a/i));
164+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/m, /a/m));
165+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/igm, /a/igm));
166+
assert.throws(makeBlock(a.deepStrictEqual, /ab/, /a/));
167+
assert.throws(makeBlock(a.deepStrictEqual, /a/g, /a/));
168+
assert.throws(makeBlock(a.deepStrictEqual, /a/i, /a/));
169+
assert.throws(makeBlock(a.deepStrictEqual, /a/m, /a/));
170+
assert.throws(makeBlock(a.deepStrictEqual, /a/igm, /a/im));
171+
172+
var re1 = /a/;
173+
re1.lastIndex = 3;
174+
assert.throws(makeBlock(a.deepStrictEqual, re1, /a/));
175+
176+
177+
// 7.4 - strict
178+
assert.throws(makeBlock(a.deepStrictEqual, 4, '4'),
179+
a.AssertionError,
180+
'deepStrictEqual === check');
181+
182+
assert.throws(makeBlock(a.deepStrictEqual, true, 1),
183+
a.AssertionError,
184+
'deepStrictEqual === check');
185+
186+
assert.throws(makeBlock(a.deepStrictEqual, 4, '5'),
187+
a.AssertionError,
188+
'deepStrictEqual === check');
189+
190+
// 7.5 - strict
191+
// having the same number of owned properties && the same set of keys
192+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, {a: 4}, {a: 4}));
193+
assert.doesNotThrow(makeBlock(a.deepStrictEqual,
194+
{a: 4, b: '2'},
195+
{a: 4, b: '2'}));
196+
assert.throws(makeBlock(a.deepStrictEqual, [4], ['4']));
197+
assert.throws(makeBlock(a.deepStrictEqual, {a: 4}, {a: 4, b: true}),
198+
a.AssertionError);
199+
assert.throws(makeBlock(a.deepStrictEqual, ['a'], {0: 'a'}));
200+
//(although not necessarily the same order),
201+
assert.doesNotThrow(makeBlock(a.deepStrictEqual,
202+
{a: 4, b: '1'},
203+
{b: '1', a: 4}));
204+
205+
assert.throws(makeBlock(a.deepStrictEqual,
206+
[0, 1, 2, 'a', 'b'],
207+
[0, 1, 2, 'b', 'a']),
208+
a.AssertionError);
209+
210+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, a1, a2));
211+
212+
// Prototype check
213+
function Constructor1(first, last) {
214+
this.first = first;
215+
this.last = last;
216+
}
217+
218+
function Constructor2(first, last) {
219+
this.first = first;
220+
this.last = last;
221+
}
222+
223+
var obj1 = new Constructor1('Ryan', 'Dahl');
224+
var obj2 = new Constructor2('Ryan', 'Dahl');
225+
226+
assert.throws(makeBlock(a.deepStrictEqual, obj1, obj2), a.AssertionError);
227+
228+
Constructor2.prototype = Constructor1.prototype;
229+
obj2 = new Constructor2('Ryan', 'Dahl');
230+
231+
assert.doesNotThrow(makeBlock(a.deepStrictEqual, obj1, obj2));
232+
233+
// primitives
234+
assert.throws(makeBlock(assert.deepStrictEqual, 4, '4'),
235+
a.AssertionError);
236+
assert.throws(makeBlock(assert.deepStrictEqual, true, 1),
237+
a.AssertionError);
238+
assert.throws(makeBlock(assert.deepStrictEqual, Symbol(), Symbol()),
239+
a.AssertionError);
240+
241+
var s = Symbol();
242+
assert.doesNotThrow(makeBlock(assert.deepStrictEqual, s, s));
243+
244+
245+
// primitives and object
246+
assert.throws(makeBlock(a.deepStrictEqual, null, {}), a.AssertionError);
247+
assert.throws(makeBlock(a.deepStrictEqual, undefined, {}), a.AssertionError);
248+
assert.throws(makeBlock(a.deepStrictEqual, 'a', ['a']), a.AssertionError);
249+
assert.throws(makeBlock(a.deepStrictEqual, 'a', {0: 'a'}), a.AssertionError);
250+
assert.throws(makeBlock(a.deepStrictEqual, 1, {}), a.AssertionError);
251+
assert.throws(makeBlock(a.deepStrictEqual, true, {}), a.AssertionError);
252+
assert.throws(makeBlock(assert.deepStrictEqual, Symbol(), {}),
253+
a.AssertionError);
254+
255+
256+
// primitive wrappers and object
257+
assert.throws(makeBlock(a.deepStrictEqual, new String('a'), ['a']),
258+
a.AssertionError);
259+
assert.throws(makeBlock(a.deepStrictEqual, new String('a'), {0: 'a'}),
260+
a.AssertionError);
261+
assert.throws(makeBlock(a.deepStrictEqual, new Number(1), {}),
262+
a.AssertionError);
263+
assert.throws(makeBlock(a.deepStrictEqual, new Boolean(true), {}),
264+
a.AssertionError);
265+
266+
152267
// Testing the throwing
153268
function thrower(errorConstructor) {
154269
throw new errorConstructor('test');

0 commit comments

Comments
 (0)