How to stub an extended ES6 class' constructor with Sinon - javascript

So I'm having a few issues with stubbing constructors, and more-so inherited classes constructors...
I'll start with some sample code snippets:
Parent.js
module.exports = class Parent {
constructor (){
// code I don't want to run during tests
}
}
MyClass.js
let Parent = require('path/to/Parent');
module.exports = class MyClass extends Parent {
// no overridden constructor
}
Mocha Test
let MyClass = require('path/to/MyClass')
...
test('test1', (done) => {
// I want to stub the MyClass/Parent constructor before creating a new instance
// of MyClass so that the constructor code in Parent doesn't run
let myClass = new MyClass();
// assertions 'n' stuff
return done();
});
...
I've tried a few things already but always find that the code in Parent constructor gets run regardless of what I do... I have a feeling it might have something to do with MyClass requiring Parent before a get a chance to stub it.
I've also tried using rewire to replace the variable in MyClass, but not joy there either; e.g.
let MyClass = rewire('path/to/MyClass');
MyClass.__set__('Parent', sinon.stub());
Any suggestions/help on how I might achieve what I'm trying to do here?

I haven't used rewire so I'm not sure why it doesn't work but stubbing the parent constructor would work fine using proxyquire:
const MockParent = sinon.stub()
const MyClass = proxyquire('../../some/path', {
'./Parent': MockParent
})

module.exports = class MyClass extends Parent {
// no overridden constructor
}
Be equal to:
module.exports = class MyClass extends Parent {
constructor (...args) {
super(...args) // be equal to Parent.call(this, ...args)
}
}
So, the processes are new MyClass() -> MyClass constructor() -> Parent.call() -> Parent constructor()

Related

Is the a shorthand to require a class and instantly initialize it?

My class exports
class MyClass {
myMethod() {
}
}
module.exports = { MyClass }
In order to use it's methods I need to
const MyClass = require(./myclass)
const myClass = new MyClass()
myClass.myMethod()
PLEASE NOTE: I don't want to change the way I'm exporting MyClass
Is there a way to initialize my class on import and amend the declaration of two variables (MyClass and myClass)?
p.s. I'm not using any transcompilers

Ecmascript 6 Class: access constructor created method of super class

I'm creating methods inside a constructor, to work with private values - this seems to be the suggested pattern to create private properties and limit the access to them.
Now I inherit from that class and within the constructor of the derived class I create another method, trying to access that. Unfortunately I get an exception TypeError: ... is not a function.
What to do? I can use the constructor as exactly that and just create a new object, but that would not be related to its classes and copying all the properties of the class and the super class etc. seems quite annoying and not like the right way. So again, what to do?
Here is an example:
class ClassA {
constructor() {
this.getValue = () => "private value";
}
}
class ClassB extends ClassA {
constructor() {
super();
this.getValue = () => `${super.getValue()}, second private value`;
}
}
const a = new ClassA();
const b = new ClassB();
console.log(`a.getValue = ${a.getValue()}`);
console.log(`b.getValue = ${b.getValue()}`);
Fiddle Check the console. The outcome I'd expect is "private value, second private value".
If someone can give me a hint, or a not super shitty workaround, I'd appreciate that.
Thanks, fea
super refers to the classes prototype. As your methods are not prototype methods but instance methods, it will unfortunately not work. However you could store the original function reference in a variable:
const orig = this.getValue.bind(this);
this.getValue = () => `${orig()}, second private value`;
However i personally rather like the _ pattern:
class ClassA {
constructor() {
this._private = "test";
}
getValue(){ return this._private; }
}
class ClassB extends ClassA {
constructor() {
super();
}
getValue(){
return `${super.getValue()}, second private value`;
}
}

How to instantiate the result of a function that returns an extended class?

How in Jasmine test this code, method getNumber
const PageFactory = SuperClass => class extends SuperClass {
getNumber() {
.......
}
I tried through new PageFactory(null) and PageFactory.getNumber, write
TypeError is not a constructor(evaluating 'PageFactory.getNumber()) or _pageFactory["default"].getNumber()
PageFactory is a factory function that creates constructor.
class TestClass { // stub class
}
const Paged = PageFactory(TestClass) // create new class using stub
const pagedInstance = new Paged() // create an instance
pagedInstance.getNumber() // <-- test getNumber method

Why isn't this allowed before super()

I have been coding in React js. I have read that in ES6 classes to access 'this' we need to first call super(props) and I would like to know why this is.Answers I have found mainly talk about Javascript being unable to know what 'this' is unless superclass is called. I would like to know what that means because outside the constructor, 'this' is recognized and we don't call super(props) each time.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { /* initial state */ };
}
}
This is really complicated, unfortunately.
The short story: access of this in a subclass before super() call is not allowed, because in ES6 this is being born in the base class, therefore super() is needed to initialize it.
For more information, refer to 15.6.2 Allocating and initializing instances1. The author is one of the few people that explains this in detail.
Here is a relevant sample from the book1 above.
Under the hood, it roughly looks as follows.
// Base class: this is where the instance is allocated
function Person(name) {
// Performed before entering this constructor:
this = Object.create(new.target.prototype);
this.name = name;
}
···
function Employee(name, title) {
// Performed before entering this constructor:
this = uninitialized;
this = Reflect.construct(Person, [name], new.target); // (A)
// super(name);
this.title = title;
}
Constructor function will return 'this' by default. According to oops concept child class always inherit 'this' object from parent class by super() call. so if we try to use this in child class without super call it will throw an error.
If we return anything except 'this' from child class then super() call is not necessary.
I have explained by some simple examples.
example 1
class A {
constructor() {
this.a = 0;
}
}
class B extends A {
constructor() {
console.log(this);
}
}
const instanceA = new A();
console.log(instanceA) // A {a: 0}
const instanceB = new B();
console.log(instanceB) // Error: Must call super constructor in derived class before
accessing 'this' or returning from derived constructor
example 2
class A {
constructor() {
this.a = 0;
}
}
class B extends A {
constructor() {
return {b: 3}
}
}
const instanceA = new A();
console.log(instanceA) // A {a: 0}
const instanceB = new B();
console.log(instanceB) // Object {b: 3}
example 3
class A {
constructor() {
this.a = 0;
}
}
class B extends A {
constructor() {
super()
}
}
const instanceB = new B();
console.log(instanceB) // B {a: 0}
The constructor method is a special method for creating and initializing an object created with a class. There can only be one special method with the name "constructor" in a class. A SyntaxError will be thrown if the class contains more than one occurrence of a constructor method. A constructor can use the super keyword to call the constructor of a parent class.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
It means if you have class MyComponent extends React.Component you always need super() call in order to make this defined.
If you don't specify a constructor method, a default constructor is used.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor#Default_constructors
Constructor of superclass should be called before this in order to finish configuration of this before subclass started configuration of this. Otherwise superclass constructor could get this modified by subclass. Superclass should not know something about subclasses. That is why super() call in constructor should be before access to this.

Referencing static members from instance method on dynamically extended JS class

I have a base ES6 class that I dynamically extend given a configuration object, like so:
class Model {
constructor () {
// ...
}
save () {
// ...
}
}
function createModelFromConfig (config) {
const Impl = class extends Model {};
Object.assign(Impl, config);
return Impl;
}
const User = createModelFromConfig({store: new DbStore()});
In the save() method on the abstract Model, I'd like to reference the static object store, which will exist on the class that extends Model. This means, of course, that I need to reference a static member but the extended class is anonymous.
Just in a quick test using the Chrome console, I tried
function X () {}
X.prototype.doSomething = function () { console.log(this.constructor); };
function Y () {}
Y.prototype = Object.create(X.prototype);
new Y().doSomething(); // function X () {}
I don't know if this is a reliable test, but it appears that this.constructor does not reference the Impl that I extended, but instead the original base class, which isn't helpful.
A less elegant way is to add Impl.prototype.Impl = Impl; so I can use this.Impl.store in my save function, but it'd be preferable if I could access the static members of the Impl class without this.
Is my prototypal test in the console inadequate? Or is there any other way to access the constructor class in an instance method from an inherited method?
In my testing, I've concluded that Y.prototype = Object.create(X.prototype); is not an adequate equivalent to the ES6 extends implementation.
In running in the Node REPL:
class X {
constructor () {}
save () { console.log(this.constructor.z); }
}
class Y extends X {}
Y.z = 'z';
new Y().save(); // 'z'

Categories

Resources