Chai: undefined does not equal undefined.

Created on 23 Jun 2016  路  4Comments  路  Source: chaijs/chai

This issue is similar to an open issue about passing in configuration to allow comparisons of null / undefined to pass to resolve to falsey in comparisons to other falsey values. But it is definitely a different issue.

If something is defined as null, then your expected value should most definitely be defined as null too because null does NOT equal undefined. Undefined and null are two different primitives.

const lhs = { variable: null };
const rhs = { };
expect(lhs).to.eql(rhs) // I would expect this to fail AND it does today.

But undefined should equal undefined.

const lhs = { variable: undefined };
const rhs = { };
expect(lhs).to.eql(rhs) // I would expect this to pass successfully BUT it does not today.

If we say that this does not pass, then we are essentially saying that undefined does not equal undefined, which just isn't true. This can turn into a nightmare when it comes to debugging tests.

Please let me know your thoughts on this issue. I am interested to hear why it was build this way. Thanks.

Most helpful comment

I understand your concern, but, semantically speaking, each one of the methods do very specific things, and these things are different from what you expect the .eql operator to do.

If you want strict comparison (===) you can use the .equal assertion and then pass rhs.variable and lhs.variable as arguments for that. That would be equivalent to lhs.variable === rhs.variable.

It appears to me that comparing the values of two expressions is different from comparing two objects, when comparing values of two expressions we just want to check if the values are the same, but when comparing two objects we want to compare everything related to those objects, including the keys it has. IMO an undeclared key means something different than a key with an undefined value. Even though accessing both return the same value, they mean different things.

Your interpretation is totally valid, I just think we interpret the semantic of assertions differently.

It is always good to hear others' opinion, thanks for sharing yours in such a constructive way, have a nice day 馃槃

All 4 comments

Hi @corybill, thanks for your issue.

Let me give you more details on this:

When using .eql you are doing a deep comparison, so you expect that every key on an object has the same value as another key on another object. So you will traverse the whole property tree recursively and compare one by one.

Your lhs object at the second example has a key called variable and your rhs is just an empty object, so when using .eql you're saying you want every property on lhs to be present and have the same value on rhs. Since rhs doesn't have the variable property the test won't pass.

If you want to check if a single property is undefined you could use expect(lhs.variable).to.be.undefined for example.

IMO this is the correct behavior since the objects do have different properties, having a key with an undefined value is definitely different from not even having that key. You can see more details on how deep-equals work by checking this module.

Here you can see that .eql runs for every enumerable key on an object and expects that that key exists on the target object too. You can check the code related to this here, here and here, for example.

Let me know if you have any further doubts.
I will close this for now, but please don't be afraid of sharing your toughts if you disagree or need anything else 馃槃

Yup, what @lucasfcosta said.

To be clear, the root of this issue--that undefined isn't the same as undeclared--is something many people consider a design flaw in JavaScript since the beginning, which can't be fixed without breaking backward compatibility. For a fun time, google "javascript undefined vs undeclared", and enjoy ~20 years of frustration.

@lucasfcosta's explanation exemplified below. The key point being that b has a property named prop but a does not. Also worth noting that Node.JS's assert library operates the same way. I imagine all alternatives do as well, because to do otherwise would be counter to the language, even if it is a flaw in the language.

let chai = require("chai");
let assert = require("assert");
let expect = chai.expect;

let a = {};
let b = {prop: undefined};

// equal to undefined
console.log(a.prop === undefined); // true
console.log(b.prop === undefined); // true

// typeof
console.log(typeof a.prop); // undefined
console.log(typeof b.prop); // undefined

// in
console.log("prop" in a); // false
console.log("prop" in b); // true

// hasOwnProperty
console.log(a.hasOwnProperty("prop")); // false
console.log(b.hasOwnProperty("prop")); // true

// assertion libraries
it("node.js assert", () => {
  assert.deepEqual(a, b); // fails
});
it("chai expect", () => {
  expect(a).to.deep.equal(b); // fails
});

I understand why it works the way it does and I also understand why it was implemented the way it was. That being said .....

const lhs = { variable: undefined };
const rhs = { };

// Example A
(lhs.variable === rhs.variable) // resolves to true whether we like it or not.

// Example B
expect(lhs).to.eql(rhs) // Resolves to false even though Example A resolves to true.

I guess I just don't like the fact that Example A resolves to true and Example B doesn't. Whether we like it or not, undeclared is explicitly resolved to an undefined value in javascript. Which means our test frameworks mean something different than the underlying javascript implementation.

Thanks so much for the quick response and a place to vent! :-) Have a good one.

I understand your concern, but, semantically speaking, each one of the methods do very specific things, and these things are different from what you expect the .eql operator to do.

If you want strict comparison (===) you can use the .equal assertion and then pass rhs.variable and lhs.variable as arguments for that. That would be equivalent to lhs.variable === rhs.variable.

It appears to me that comparing the values of two expressions is different from comparing two objects, when comparing values of two expressions we just want to check if the values are the same, but when comparing two objects we want to compare everything related to those objects, including the keys it has. IMO an undeclared key means something different than a key with an undefined value. Even though accessing both return the same value, they mean different things.

Your interpretation is totally valid, I just think we interpret the semantic of assertions differently.

It is always good to hear others' opinion, thanks for sharing yours in such a constructive way, have a nice day 馃槃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sverrirs picture sverrirs  路  3Comments

danthegoodman picture danthegoodman  路  3Comments

leifhanack picture leifhanack  路  4Comments

meeber picture meeber  路  5Comments

zzzgit picture zzzgit  路  3Comments