Vitest considers `-0` and `+0` to be different values
Describe the bug
I have a utility that may return -0.
This is technically a distinct value from +0, but per IEEE_754, "negative zero" and "positive zero" should be considered to be equal in numerical comparisons.
But vitest's .toBe() and .toEqual() methods appears to not consider them to be equal, causing unit tests to fail.
Reproduction
https://stackblitz.com/edit/vitest-dev-vitest-dgy7dtwc?file=test%2Fbasic.test.ts
System Info
The issue is reproduced in StackBlitz above, but here is my system info.
System:
OS: Linux 6.8 Ubuntu 22.04.4 LTS 22.04.4 LTS (Jammy Jellyfish)
CPU: (20) x64 13th Gen Intel(R) Core(TM) i5-13600K
Memory: 19.04 GB / 31.12 GB
Container: Yes
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 20.13.1 - ~/.nvm/versions/node/v20.13.1/bin/node
Yarn: 1.22.22 - ~/.nvm/versions/node/v20.13.1/bin/yarn
npm: 10.5.2 - ~/.nvm/versions/node/v20.13.1/bin/npm
pnpm: 8.15.9 - ~/.nvm/versions/node/v20.13.1/bin/pnpm
npmPackages:
@vitejs/plugin-react-swc: ^3.8.0 => 3.8.0
@vitest/coverage-v8: ^3.0.6 => 3.0.6
@vitest/ui: ^3.0.6 => 3.0.6
vite: ^6.1.0 => 6.1.0
vitest: ^3.0.6 => 3.0.6
Used Package Manager
pnpm
Validations
- [x] Follow our Code of Conduct
- [x] Read the Contributing Guidelines.
- [x] Read the docs.
- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [x] The provided reproduction is a minimal reproducible example of the bug.
Digging into vitest's codebase, the root cause is clear:
-
.toBe()usesObject.is(), which finds-0and+0to not be the same: https://github.com/vitest-dev/vitest/blob/470cbec1f91bd3cb0aa604077fa288c4a6e1c2b9/packages/expect/src/jest-expect.ts#L146 -
.toEqual()also usesObject.is(): https://github.com/vitest-dev/vitest/blob/470cbec1f91bd3cb0aa604077fa288c4a6e1c2b9/packages/expect/src/jest-utils.ts#L121-L123
I think .toBe() kinda makes sense to fail here, because -0 truly is not +0. But I think toEqual() should pass.
I'm not sure if it's obvious to say which is right 🤔
I'd assume Jest's equality has same behavior. On node, equal/deepEqual passes since it uses ==, while strictEqual/strictDeepEqual fails since it uses Object.is. https://nodejs.org/docs/v22.14.0/api/assert.html#assertdeepequalactual-expected-message
> assert.equal(+0, -0)
undefined
> assert.strictEqual(+0, -0)
Uncaught AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 0
- -0
Quick workaround would be to use custom equality tester like this https://stackblitz.com/edit/vitest-dev-vitest-firxqjfs?file=test%2Fbasic.test.ts
expect.addEqualityTesters([
function (a, b) {
if (typeof a === 'number' && typeof b === 'number') {
return a === b;
}
},
]);
Thanks! Adding a custom equality tester is a good solution for my use case.
I noticed a code comment saying the .toEqual() logic was copied from underscore. I suspect the existing behaviour is ubiquitous and am therefore wary of changing it, even if it feels wrong. Who knows what implicitly relies on it.
I'm happy to close this issue if y'all prefer to leave the logic as-is.
If this issue needs to be resolved, we can refer to the issue #5744 and PR for handling -0 and -NaN in the View.
Docs Ref
We have experienced this issue to. Our fix is to add the desired sign (e.g. -0), but our formatter now removes the -.