Reputation: 4621
So I'm doing a little bit of experimental programming to ensure I understand how certain things work, and I'm coming across an error which I don't understand. I can't see any obvious solutions for this problem using Google or on here specifically.
I'm trying to confirm to myself exactly how jest.spyOn()
works when applied to module imports. But right now the answer is that it just doesn't.
spy.js:
export const foo = () => {
console.log("called foo");
};
export const bar = () => {
console.log("called bar");
};
spy.spec.js:
import * as Foo from "./spy";
import { jest } from "@jest/globals";
test("call foo", () => {
jest.spyOn(Foo, "bar");
});
package.json:
{
"type": "module",
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
},
"dependencies": {
"jest": "^27.0.1"
}
}
When I run this test, I get the following error: TypeError: object.hasOwnProperty is not a function
. I'm running node v15.12.0.
Can somebody explain to my
Edit
It has been suggested that the problem is to do with the module import lacking a prototype
property. This is not the issue - see below
import * as Foo from "./spy";
import { jest } from "@jest/globals";
test("SpyOn plain object", () => {
const bar = { baz: () => {} };
expect(bar.prototype).toBeUndefined(); // Pass
jest.spyOn(bar, "baz"); // No error
});
test("SpyOn import", () => {
expect(Foo.prototype).toBeUndefined(); // Pass
jest.spyOn(Foo, "foo"); // Error!
});
Upvotes: 3
Views: 6754
Reputation: 21
I just added the missing method.
Foo.hasOwnProperty = () => Object.hasOwnProperty;
jest.spyOn(Foo, "foo")
Not ideal... but worked. I was using Vue and Jest and solved the error for me.
Upvotes: 2
Reputation: 10460
I debugged spyOn
to see what happens.
Since Foo
is an object of type Module
, it doesn't have an hasOwnProperty
method (see this answer regarding module namespace exotic objects).
Hence, spyOn
throws in the following line (the object
is the module Foo
):
const isMethodOwner = object.hasOwnProperty(methodName);
If we change it to:
const isMethodOwner = Object.prototype.hasOwnProperty.call(object, methodName);
It would work, but we'll encounter another error(s) in the code that follows, for example here:
object[methodName] = original;
Which throws:
TypeError: Cannot assign to read only property 'bar' of object '[object Module]'
So maybe it's "better" to just reassign object
?:
object = Object.assign({}, object);
In which case the following test passes:
test("call bar", () => {
const spy = jest.spyOn(Foo, "bar");
spy();
expect(spy).toHaveBeenCalled();
});
But the following test fails, though I can't tell if it should pass at all?:
test("call bar", () => {
const spy = jest.spyOn(Foo, "bar");
Foo.bar();
expect(spy).toHaveBeenCalled();
});
Let us open a jest issue?
Upvotes: 4