Javascript inheritance puzzles me - javascript

I am trying to understand JavaScript inheritance and I hit a road block. I am not familiar with JavaScript's prototype based inheritance. I am still using old NodeJS version so I can't use classes.
Could you tell me why following code prints the same values for different workers and how can I prevent it?
var Manager = require('./manager');
var worker1 = new Manager.getWorker('foo');
var worker2 = new Manager.getWorker('bar');
console.log(worker1.config);
console.log(worker2.config);
//Implementation Details
//Manager
(function() {
'use strict';
function Manager() {
}
Manager.getWorker = function(slug) {
var Worker = null;
try {
Worker = require('./workers/' + slug);
Worker = new Worker();
} catch (e) {
console.log('Worker error: cannot initiate worker');
}
return Worker;
};
module.exports = Manager;
})();
//foo.js
(function () {
var _abstract = require('../abstract_worker');
var _util = require('util');
var config = {
"slug": "foo",
"name": "Foo Worker"
};
function Foo() {
this.config = config;
Foo.super_.apply(this,arguments);
}
_util.inherits(Foo, _abstract);
module.exports = Foo;
})();
//bar.js
'use strict';
(function () {
var _abstract = require('../abstract_worker');
var _util = require('util');
var config = {
"slug": "bar",
"name": "Bar Worker"
};
function Bar() {
this.config = config;
Bar.super_.apply(this,arguments);
}
_util.inherits(Bar, _abstract);
module.exports = Bar;
})();
(function() {
'use strict';
//config
var _ = require('lodash');
var default_config = {
number_of_job: 1
};
function AbstractWorker() {
if (!this.config) {
this.config = {};
}
if (!_.isString(this.config.slug)) {
throw new Error('config.slug is undefined.');
}
if (!_.isString(this.config.name)) {
throw new Error('config.name is undefined.');
}
this.config = _.merge(default_config, this.config);
}
module.exports = AbstractWorker;
})();
EDIT: Thanks to #Bergi I found out the source of the error. It seems Lodash's merge recursively merges own and inherited enumerable string keyed properties of source objects into the destination object. In my code it seems I also made a typo and wrote in wrong order. I just leave it as it is maybe it can help others in the long run.

this.config = _.merge(default_config, this.config);
is your problem. It assigns the same default_config object to all workers.

Related

sinon wont replace dependency

I am trying to write a unit test for the class in the example below.
const DependencyClass = require('../../../../Dependency/src/index').DependencyClass;
const string = 'test';
class FirstClass {
async getResult() {
const dependency = new DependencyClass();
const result = dependency.getResult(string);
return result;
}
}
module.exports = {
FirstClass
};
I am trying to stub the getResult() method of the DependencyClass class so it returns a predefined value when called from my unit tests but cant figure out how to do it.
const FirstClass = require('../../lib/FirstClass ').FirstClass;
describe('FirstClass.js', function() {
describe('getResult()', function() {
it('throws an exception if the result is not returned', async function() {
const firstClass = new FirstClass();
sinon.replace(firstClass.getResult, 'DependencyClass.getResult', function() {
const fakeResult =
[
'test1',
'test2'
];
return fakeResult;
});
const expectedResult =
[
'test1',
'test2'
];
const result = await firstClass.getResult();
expect(result).to.deep.eq(expectedResult);
});
});
});
afterEach(function() {
sinon.restore();
});
I understand that DependencyClass.getResult is not a property of the firstClass.getResult object but I am struggling to understand how sinon should be used in this context.
If you want to stub method getResult() from class DependencyClass, then you need to create stub from it: sinon.stub(DependencyClass.prototype, 'getResult');.
Here the complete example.
Note: I remove all async await, because it is unnecessary for this simple example.
File DependencyClass.js
// #file DependencyClass.js
class DependencyClass {
getResult() {
return 'xxx';
}
}
module.exports = { DependencyClass };
File FirstClass.js
// #file FirstClass.js
const { DependencyClass } = require('./DependencyClass.js');
const string = 'test';
class FirstClass {
getResult() {
const dependency = new DependencyClass();
return dependency.getResult(string);
}
}
module.exports = { FirstClass };
Test spec file
// #file stackoverflow.spec.js
const sinon = require('sinon');
const { expect } = require('chai');
const { DependencyClass } = require('./DependencyClass');
const { FirstClass } = require('./FirstClass');
describe('FirstClass.js', function () {
describe('getResult()', function () {
it('throws an exception if the result is not returned', function () {
// Create fake response.
const fakeResult = ['test1', 'test2'];
// Create stub DependencyClass method getResult().
const stubDependencyGetResult = sinon.stub(DependencyClass.prototype, 'getResult');
stubDependencyGetResult.returns(fakeResult);
// Initiate first class.
const firstClass = new FirstClass();
// Call firstClass method getResult.
const result = firstClass.getResult();
// Check whether the result is correct.
expect(result).to.deep.equal(fakeResult);
// Verify stub get called.
expect(stubDependencyGetResult.calledOnce).to.equal(true);
// Restore stub.
stubDependencyGetResult.restore();
});
});
});
When I run it using mocha:
$ mocha stackoverflow.spec.js
FirstClass.js
getResult()
✓ throws an exception if the result is not returned
1 passing (6ms)
$
Hope this helps.

Why *don't* I need methods to set or read class attributes?

In putting together getters and setters I realized that they aren't required.
What am I missing? How to ensure that when i call attributes they are the ones on the class ?
'use strict';
const { Book } = require('../app');
const { Page } = require('../page');
const { Bookmark } = require('../bookmark');
//
const chai = require('chai');
const expect = chai.expect;
it('has a bookmark', function () {
let bookmark = new Bookmark('bookmark');
bookmark.material = 'paper';
bookmark.mm='a';
expect(bookmark.mm).to.equal('a');
expect(bookmark.material).to.equal('paper');
})
#../bookmark.js
class Bookmark {
constructor(name) {
this.name = name;
}
}
exports.Bookmark = Bookmark;

How to verify that a constructor was called using sinon

I need to assert whether a constructor was called using sinon. Below is how I can create a spy.
let nodeStub: any;
nodeStub = this.createStubInstance("node");
But how can I verify that this constructor was called with the relevant parameters? Below is how the constructor is actually called.
node = new node("test",2);
Any help would be much appreciated.
Below is the code I have.
import {Node} from 'node-sdk-js-browser';
export class MessageBroker {
private node: Node;
constructor(url: string, connectionParams: IConnectionParams) {
this.node = new Node(url, this.mqttOptions, this.messageReceivedCallBack);
}
}
Given the following code myClient.js:
const Foo = require('Foo');
module.exports = {
doIt: () => {
const f = new Foo("bar");
f.doSomething();
}
}
You can write a test like this:
const sandbox = sinon.sandbox.create();
const fooStub = {
doSomething: sandbox.spy(),
}
const constructorStub = sandbox.stub().returns(fooStub);
const FooInitializer = sandbox.stub({}, 'constructor').callsFake(constructorStub);
// I use proxyquire to mock dependencies. Substitute in whatever you use here.
const client2 = proxyquire('./myClient', {
'Foo': FooInitializer,
});
client2.doIt();
assert(constructorStub.calledWith("bar"));
assert(fooStub.doSomething.called);
//module.js
var Node = require('node-sdk-js-browser').Node;
function MessageBroker(url) {
this.node = new Node(url, 'foo', 'bar');
}
module.exports.MessageBroker = MessageBroker;
//module.test.js
var sinon = require('sinon');
var rewire = require('rewire');
var myModule = require('./module');
var MessageBroker = myModule.MessageBroker;
require('should');
require('should-sinon');
describe('module.test', function () {
describe('MessageBroker', function () {
it('constructor', function () {
var spy = sinon.spy();
var nodeSdkMock = {
Node: spy
};
var revert = myModule.__set__('node-sdk-js-browser', nodeSdkMock);
new MessageBroker('url');
spy.should.be.calledWith('url', 'foo', 'bar');
revert();
});
});
});

How to stub a dynamic object method using Sinon.js?

I have following module.
var Sendcloud = require('sendcloud');
var sc = new Sendcloud("key1","key2","key3");
var service = {};
service.restorePassword = function (params, cb) {
if (!params.to || !params.name || !params.token) {
throw "Miss params"
}
var defaultTemplate = adminBaseUrl + "reset/token/" + params.token;
var subject = params.subject || "Letter";
var template = params.template || defaultTemplate;
// Send email
sc.send(params.to, subject, template).then(function (info) {
console.log(info)
if (info.message === "success") {
return cb(null, "success");
} else {
return cb("failure", null);
}
});
};
module.exports = service;
I experience problems stubbing sc.send method. How to correctly cover this point using sinon.js? Or maybe I need to replace sendcloud module?
You need to use proxyquire module or rewire module.
Here an example of using proxyquire
var proxyquire = require('proxyquire');
var sinon = require('sinon');
var Sendcloud = require('sendcloud');
require('sinon-as-promised');
describe('service', function() {
var service;
var sc;
beforeEach(function() {
delete require.cache['sendcloud'];
sc = sinon.createStubInstance(Sendcloud);
service = proxyquire('./service', {
'sendcloud': sinon.stub().returns(sc)
});
});
it('#restorePassword', function(done) {
sc.send.resolves({});
var obj = {
to: 'to',
name: 'name',
token: 'token'
};
service.restorePassword(obj, function() {
console.log(sc.send.args[0]);
done();
});
});
});

NodeJS module export

I have a simple http client module(api.js) which is returning a promise as in the followings:
exports.endpoint = '';
exports.GET = function(args){
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.POST = function(args){
args.method = 'POST';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.PUT = function(args){
args.method = 'PUT';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.DELETE= function(args){
args.method = 'DELETE';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
var asyncApiCall = function(args){
var rp = require('request-promise');
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
}
return rp(options);
};
and I use the module like this:
var api = require('./api.js');
var args = {
uri : '/posts'
}
api.endpoint = 'http://localhost:3000';
api.GET(args)
.then(function(res){
console.log(res);
}, function(err){
console.log(err);
});
Now, I want to improve the module as much as possible. Is there any way to not repeat the export.functionName? I found the module.exports in NodeJS, but I am not sure how to use it in this case. How can I set the endpoint variable once in asyncApiCall function instead of all the other functions that return asyncApiCall?
Just another style:
var rp = require('request-promise'); // Put it here so you don't have to require 1 module so many times.
var asyncApiCall = function(args) {
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
};
return rp(options);
};
// Let's hack it.
var endpoint = '';
var apis = {};
['GET', 'POST', 'PUT', 'DELETE'].forEach(function(method) {
apis[method] = function(args) {
args.method = method;
args.uri = endpoint + args.uri;
return asyncApiCall(args);
}
});
module.exports = apis;
module.exports.endpoint = '';
A lot of people chose to put their export methods on a new object and export via module.exports, e.g.
var myExports = {
get: function () {},
post: function () {}
}
module.exports = myExports;
As for module.exports vs exports
It looks like it may be appropriate to set up a full constructor with your methods tied to it, like so:
var requests = function (endpoint) {
this.endpoint = endpoint;
}
requests.prototype.GET = function (args) {
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
}
// And so on
module.exports = requests;
And then call it like so:
var api = require('./api.js');
var endpoint = new api("http://localhost:3000");
endpoint.GET()
Wrap this in a class and export a new instance of it
function Module() {
}
Module.prototype.GET = function () {}
module.export = new Module()
// or
module.export = Module
// to call the constructor for your endpoint variable.

Categories

Resources