Reputation: 20446
During unit testing, I want to Mock class Foo with a class called Bar. I have to change a propriety in the class Foo for the test.
This is what I tried. It should print:
Foo
Bar
class Foo {
id = 'foo'
constructor() {
console.log(this.id)
}
}
const Bar = { ...Foo }
Bar.prototype.id = 'Bar'
new Foo()
new Bar()
I get instead:
error: Uncaught TypeError: Cannot set property 'id' of undefined
I know I can achieve this by creating a new class Bar extends Foo but is there other possible solutions ?
Upvotes: 4
Views: 93
Reputation: 31833
Object Spread {...o}
only considers enumerable properties.
From MDN (emphasis mine):
Spread in object literals The Rest/Spread Properties for ECMAScript proposal (ES2018) added spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.
The prototype
property of classes and function is not enumerable. Therefore, in const Bar = {...Foo}
, Bar
will not have a prototype
property. That is precisely why you get
error: Uncaught TypeError: Cannot set property 'id' of undefined
Now, to get the code closer to what you want, you could write
const Bar = {...Foo, prototype: Foo.prototype};
However, now Bar
will behave differently from Foo
because its prototype
property will be enumerable.
So we could revise this and write
const Bar = {...Foo};
Object.defineProperty(Bar, 'prototype', {value: Foo.protoype, enumerable: false});
// `enumerable` defaults to `false` using this API, just being explicit for exposition.
However, your next statement, new B
will still fail because B
is still not a class object or a function object and therefore cannot be used with the new operator.
Without more context it is difficult to tell why you actually want to do this.
The most straightforward approach would be to write the following
class Foo {
id = 'Foo';
constructor() {
console.log(this.id);
}
}
class Bar extends Foo {
id = 'Bar';
}
new Foo() // logs 'Foo'
new Bar() // logs 'Bar'
If you would like to avoid using inheritance, there are other alternatives, but it is not clear which ones are appropriate because the question lacks sufficient context to determine this. For example, if you only want to mock objects created via new Foo
, then you could mock them trivially using a helper function that just overwrites the property with the test value, if you want to mock Foo
itself, that is to say the class object, then the alternatives would be different.
Upvotes: 2
Reputation: 7725
Instance properties in NodeJS and Babel are not actually set to the prototype. They are set in the constructor.
Check this:
class Foo {
id = 1
}
new Foo().hasOwnProperty('id') // => true
An object will only lookup a property on its prototype if it's not defined within itself, so overriding the prototype won't work.
If you want to avoid class
syntax, you could do something like this:
function Bar() {
const o = new Foo()
o.id = 'bar'
return o
}
new Bar().id // => 'bar'
Upvotes: 1