unexpected
unexpected copied to clipboard
Strange equals comparison testing jest mock calls - workaround incl
Per se, unexpected is great for (amongst other things) comparing more complex structures - including the calls to a mock.
There is a glitch, however: The following line fails w/ a somewhat strange error message:
expect(resp.status.mock.calls, 'to equal', [[500]]); // one call, w/ 500 as one and only arg
msg is:
UnexpectedError:
expected [ [ 500 ] ] to equal [ [ 500 ] ]
Mismatching constructors Array should be Array
What does work is this:
const normalizeArray=(obj) => (Array.isArray(obj) ? [].concat(obj).map(c => normalizeArray(c)): obj); // hopefully an utils.js ;-)
expect(normalizeArray(resp.status.mock.calls), 'to equal', [[500]]);
I didn't yet figure out what the root cause is. And there's a workaround, so it's not really critical.
Just wanted to let you know about the workaround (and I'm sure grateful if s/o can point me to an existing solution).
That looks interesting, we will definitely look into that. Might it just be a type that is trying to fake being an array?
I don't think I have a setup where I can easily reproduce this. Seems like we will only emit this error message when resp.status.mock.calls.constructor !== [[500]].constructor
, which seems to indicate that something is trying to masquerade as an array, as @sunesimonsen suggests. Could also be that the left-hand side object originates from another vm
context or something. Could you try to find out what resp.status.mock.calls.constructor
is by logging resp.status.mock.calls.constructor.toString()
right before the assertion?
Also, does expect(resp.status.mock.calls, 'to satisfy', [[500]]);
pass?
console.log(resp.status.mock.calls.constructor.toString())
yields
function Array() { [native code] }
and
expect(resp.status.mock.calls, 'to satisfy', [[500]]);
does indeed work - thanks so much already!
Jest does use the vm
module in a bunch of places, so that's probably it:
[jest]$ ag vm | grep -P 'require|import' | grep -v __tests__
types/Environment.js:12:import type {Script} from 'vm';
types/Transform.js:11:import type {Script} from 'vm';
packages/jest-environment-jsdom/src/__mocks__/index.js:9:const vm = require.requireActual('vm');
packages/jest-environment-jsdom/src/index.js:9:import type {Script} from 'vm';
packages/jest-environment-node/src/index.js:10:import type {Script} from 'vm';
packages/jest-environment-node/src/index.js:15:import vm from 'vm';
packages/jest-leak-detector/src/index.js:14:import vm from 'vm';
packages/jest-runtime/src/script_transformer.js:19:import vm from 'vm';
packages/jest-repl/src/cli/repl.js:19:import vm from 'vm';
Each vm
context will have its own Array
global (similar to how frames in are isolated from one another in the browser):
const expect = require('./lib/');
const vm = require('vm');
const foreignArray = vm.runInNewContext(`['foo']`);
expect(foreignArray, 'to equal', ['foo']);
Output:
UnexpectedError:
expected [ 'foo' ] to equal [ 'foo' ]
Mismatching constructors Array should be Array
Not sure what we should do about that. I don't think we should try to change the semantics, but we could display a friendlier error message that hints at this.
Thanks so much already! I think we (rather: you!) did the most important thing - put information out helping anyone encountering the same.
Concerning what to do about it: when calling expect(Array.isArray(foreignArray), 'to be true')
I'll pass (i.e. foreignArray is an array at least in that respect). Would that be a way forward?
I wonder if there is another option here - at some point as part of the jest-unexpected
work I think I created a small bridge for unexpected-sinon
to allow using it's named assertions ("to have a call satisfying"
) against the jest.mock() objects. A detail like this could be abstracted that way.
On a broader note, this is sort of an interesting case and it's consistent with what I've seen of some of the niggles that occur due to the extent to which jest tries to isolate things. As per @papandreou no idea what we can do about this more generally, but it's with pondering this some.