-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
assert.deepStrictEqual is fooled by strange dates #10258
Comments
Tested on master, for
But
But in this case the more reasonable behavior should throw IMHO. I can open a PR adding a special case in assert it that seems reasonable to others. |
I'm open to being persuaded on this, but given what we have here so far, I'm not convinced this is a bug. The two objects have identical properties, including the This seems basically the same as this: const assert = require('assert');
function Foo() {}
function Bar() {}
Bar.prototype = Foo.prototype;
const f = new Foo();
const b = new Bar();
assert.deepStrictEqual(f, b); If we decide that |
It's not quite the same as your example, as
@joyeecheung, yep I meant |
Just did a quick research across some popular assertion libraries that I can think of(omitted 'use strict';
const assert = require('assert');
const chaiAssert = require('chai').assert;
const chaiExpect = require('chai').expect;
const expect = require('expect.js');
const should = require('should');
const codeExpect = require('code').expect;
const mustExpect = require('must');
const unexpectedExpect = require('unexpected');
function FakeDate() {};
FakeDate.prototype = Date.prototype;
const d = new Date();
const f = new FakeDate();
// to make sure I use these libs properly
// const d = { a: 1, b: 2 };
// const f = { a: 1, b: 2 };
let tests = {
'assert': () => assert.deepEqual(d, f),
'chaiAssert': () => chaiAssert.deepEqual(d, f),
'should': () => should.deepEqual(d, f),
'codeExpect': () => codeExpect(d).to.equal(f),
'chaiExpect': () => chaiExpect(d).to.deep.equal(f),
'mustExpect': () => mustExpect(d).to.eql(f),
'unexpectedExpect': () => unexpectedExpect(d, 'to equal', f),
'expect': () => expect(d).to.eql(f)
}
for (const name in tests) {
try {
tests[name]();
console.log('\n### ' + name + ' does not throw');
} catch(e) {
console.log('\n### ' + name + ' throws:');
console.log(e.message);
}
} The results are:
|
@Trott And for the |
Hmmm... On the one hand: For both objects, it's all tucked away behind getters and setters, and those getters and setters are all identical. But on the other hand: You certainly have a point that this behavior is kind of surprising and non-intuitive, at least at first blush. And the fact that six out of six userland assertion libraries throw on this would seem to back that up. I can go either way on this one right now. A PR might help clarify things. So I'll stop getting in the way. :-D |
OK, I think I will try to read other libraries' code first and figure out why they all choose(or perhaps accidentally choose) to throw :). |
Another unexpected result: class MyDate extends Date {
constructor(...args) {
super(...args)
this.myProp = 'value'
}
}
var date1 = new Date('2016')
console.log(date1)
// 2016-01-01T00:00:00.000Z
var date2 = new MyDate('2016')
console.log(date2)
// { 2016-01-01T00:00:00.000Z myProp: 'value' }
assert.deepStrictEqual(date1, date2) // does not throw Relevant lines from assert: Lines 152 to 153 in 4f97a14
|
Not quite relavent to this issue, this just came up to me while I am digging around the code: assert.deepEqual(new Set([1, 2]), new Set([1, 2, 3])); // does not throw
assert.deepEqual(new Map([['a', 1], ['b', 2]]), new Map([['a', 1], ['b', 3]])); // does not throw I know assert has a stability index of 3, so it's just a thought :P. Since we are using more and more ES6 in core and V8 have started to optimize iterators, adding checks for iterables might be a nice-to-have in the future. |
@joyeecheung That last example is why |
@Fishrock123 Uh, which one do you mean? |
I looked into the source code of other libraries, turns out
And in the user-land modules, I think in addition to checking the prototype, we need to check the types returned by |
I'm trying to put a PR together but there are always some really bizarre cases that I could break, like these introduced by 00a7456: assert.doesNotThrow(makeBlock(a.deepEqual, new Number(1), {}),
a.AssertionError);
assert.doesNotThrow(makeBlock(a.deepEqual, new Boolean(true), {}),
a.AssertionError); |
@joyeecheung those are really weird tests, I would assume those should throw. |
@joyeecheung I meant assert.doesNotThrow(makeBlock(a.deepEqual, new Number(1), {}),
a.AssertionError);
assert.doesNotThrow(makeBlock(a.deepEqual, new Boolean(true), {}),
a.AssertionError); These look correct to me. |
@Fishrock123 the docs support what you are saying (and even warn about this surprising behaviour), fair enough. I guess |
Yes. This is one of many reasons why you should probably never use the Object forms of Simple Primitives. |
I am thinking more on this, is there any actual use case for someone to want |
That is why we have |
@Fishrock123 I am 50/50 on this, given it's a 50/50 result among the user land modules that I've tested :). I am currently inclined to leave
Trying to make a PR now! :) |
Refactors _deepEqual and fixes a few code paths that lead to behaviors contradicting what the doc says. Before this commit certain types of objects (Buffers, Dates, etc.) are not checked properly, and can get away with different prototypes AND different enumerable owned properties because _deepEqual would jump to premature conclusion for them. Since we no longer follow CommonJS unit testing spec, the checks for primitives and object prototypes are moved forward for faster failure. Improve regexp and float* array checks: * Don't compare lastIndex of regexps, because they are not enumerable, so according to the docs they should not be compared * Compare flags of regexps instead of separate properties * Use built-in tags to test for float* arrays instead of using instanceof Use full link to the archived GitHub repository. Use util.objectToString for future improvements to that function that makes sure the call won't be tampered with. Refs: nodejs#10282 (comment) Refs: nodejs#10258 (comment)
Refactors _deepEqual and fixes a few code paths that lead to behaviors contradicting what the doc says. Before this commit certain types of objects (Buffers, Dates, etc.) are not checked properly, and can get away with different prototypes AND different enumerable owned properties because _deepEqual would jump to premature conclusion for them. Since we no longer follow CommonJS unit testing spec, the checks for primitives and object prototypes are moved forward for faster failure. Improve regexp and float* array checks: * Don't compare lastIndex of regexps, because they are not enumerable, so according to the docs they should not be compared * Compare flags of regexps instead of separate properties * Use built-in tags to test for float* arrays instead of using instanceof Use full link to the archived GitHub repository. Use util.objectToString for future improvements to that function that makes sure the call won't be tampered with. PR-URL: #11128 Refs: #10282 (comment) Refs: #10258 (comment) Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Michaël Zasso <[email protected]>
Add checks for the built-in type tags to catch objects with faked prototypes. See https://tc39.github.io/ecma262/#sec-object.prototype.tostring for a partial list of built-in tags. Fixes: nodejs#10258
Add checks for the built-in type tags to catch objects with faked prototypes. See https://tc39.github.io/ecma262/#sec-object.prototype.tostring for a partial list of built-in tags. Fixes: nodejs#10258 PR-URL: nodejs#10282 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
assert.deepStrictEqual
(andstrictEqual
) is fooled by the following code:I think it's reasonable to expect that
assert.deepStrictEqual
should considerd
andf
as unequal.I have not tested on Node master, however there are no relevant changes in
assert.js
between 6.9.1 and master.The text was updated successfully, but these errors were encountered: