Skip to content
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

Strange equals comparison testing jest mock calls - workaround incl #445

Open
sebastianrothbucher opened this issue Apr 10, 2018 · 6 comments

Comments

@sebastianrothbucher
Copy link

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).

@sunesimonsen
Copy link
Member

That looks interesting, we will definitely look into that. Might it just be a type that is trying to fake being an array?

@papandreou
Copy link
Member

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?

@sebastianrothbucher
Copy link
Author

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!

@papandreou
Copy link
Member

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.

@sebastianrothbucher
Copy link
Author

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?

@alexjeffburke
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants