Getting instance of node module while test driving codeal - node.js

I am creating a factory method where i am returning an instance of a node module
var dal1 = require('../dal/dal1');
var dal2 = require('../dal/dal2');
exports.createDAL(role){
switch(role){
case "XYZ": return dal1;
case "ABC": return dal2
}
};
Using Mocha as the TDD frame work, When I get the object returned,I get the value of returnObject.constructor.name as object instead of dal1 or dal2. Any pointers?

So I went ahead with using the node-module 'util'. I was able to get the expected value instead of just 'Object'
I created a DAL object
//dal.js
function dal(){
}
dal.prototype.BLAH = function(){
//logic here
}
module.exports = dal
//dal1.js
var inherits = require('util').inherits;
function dal1(){
dal1.call(this);
}
inherits(dal1,dal);
module.exports = dal1;
//similarly dal2.js
//finally in factory
var dal1 = require('./dal1');
var dal2 = require('./dal2');
switch(role){
case 'XYZ': return new dal1();
case 'ABC' : return new dal2();
}

Related

Mocha testing javascript with inheritance

I am new to Mocha and Node but trying to write some Mocha tests on some JavaScript classes.
I have the following class:
function FormField() {
}
FormField.prototype.sanitizeFieldValue = function(value) {
if (value == null || value.replace == null) {
return null;
}
return value
.replace(/ /g, " ")
.replace(/&/g, '&')
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/[\n\r]+/g, " ");
};
module.exports = FormField;
When I run my mocha tests on this file, everything works fine and the tests pass.
var expect = require("chai").expect;
var formfield = require("FormField");
describe("new Form Field Tests", function() {
var ff = new formfield();
describe("sanitizeFieldValue", function() {
it("escapes apostrophe", function() {
expect(ff.sanitizeFieldValue("'")).to.equal("\\\'");
});
});
});
However, I have another file which references the first:
TargetDateField.prototype = new FormField();
function TargetDateField() {
// some functions
}
module.exports = TargetDateField;
But I am not sure how to test this
I have tried the following but I keep getting FormField is not defined.
var expect = require("chai").expect;
var FormField = require("FormField").FormField;
var targetdatefield = require("TargetDateField");
Any ideas on how to resolve this?
Everywhere where you want to use FormField you require the module that defines it.... except in your TargetDateField.js file, where you don't require it. That's why you are getting an error. You need to require it there too:
var FormField = require("FormField");
TargetDateField.prototype = new FormField();
// etc...
By the way, I strongly suggest writing your code to use relative paths when you want to load other modules that are part of the same package. At first glance, I'd expect require("FormField") to load something from node_modules. (Like when you do require("chai").)

How can I stub a constructor in node.js using sinon in a Cloud Functions for Firebase project?

I'm using GeoFire in a Cloud Functions for Firebase project I want to unit test.
In my original code GeoFire is used like this:
GeoFire = request('geofire');
...
var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then( () => {
console.log("Added store " + storeId + " to GeoFire" );
return Promise.resolve();
});
I need to stub both the call to the GeoFire constructor and the GeoFire().set() method.
I tried:
const geofireStub = sinon.stub(GeoFire, 'set').resolves();
But I received the error:
TypeError: Cannot stub non-existent own property set
I also tried:
const setStub = sinon.stub().resolves();
const geofireStub = sinon.stub(GeoFire).returns({set: setStub});
And I receive the error:
TypeError: Cannot stub non-existent own property set
Both errors happen at the geofireStub line.
Reading the sinon documentation I understood that I can stub an object's methods. However in this case GeoFire isn't an object; it is a constructor function. So I don't really know how can I stub it without having an associated object.
Thanks!
You need something like this, using rewire:
// target.js
var GeoFire = require('geofire');
var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then(() => {
console.log("Added store " + storeId + " to GeoFire" );
return Promise.resolve();
});
// test.js
var GeoFire = require('geofire');
var rewire = require('rewire')
var target = rewire('./target')
describe('target', () => {
it('test case', () => {
// arrange
// configure instance
var geoFireStub = sinon.createStubInstance(GeoFire)
geoFireStub.set.resolves()
// configure constuctor
var GeoFireMock = sinon.stub().returns(geoFireStub)
// 'GeoFire' is a mocked variable here
var revert = rewire('GeoFire', GeoFireMock)
// act (call tested module)
target()
// assert (should is just for example)
should(GeoFireMock).calledWithNew(/* params*/)
should(geoFireStub.set).calledWith(/* params*/)
//cleanup (rewire and stubs, prefer to use sandbox)
revert();
...
})
})
GeoFire is the constructor, but set is an instance method.
You should stub GeoFire.prototype I believe.
sinon.stub(GeoFire.prototype, 'set').resolves();

require module does not respond object as singleton pattern in some cases

file_a.js is dependency of file_b.js and file_c.js. Please take a look in file_c.js, there is weird thing there.
file_a.js
module.exports = {
test: null,
setTest: function(a){
this.test = a;
},
getTest: function(){
return this.test
},
}
file_b.js
var a = require('./file_a')
var b = {
print: function(someVar){
console.log(someVar);
}
}
a.setTest(b)
file_c.js
this way will work
var a = require('./file_a')
console.log(typeof a.getTest().print) //function
this way will NOT work
var a = require('./file_a').getTest()
console.log(typeof a.print) //Cannot read property 'print' of null
Both of your examples for file_c.js throw TypeError: Cannot read property 'print' of null.
Module from file_b.js sets test property from module file_a.js on its initialization, and in your snippets it never gets initialized. To fix this, you need:
var a = require('./file_a');
require('./file_b'); // now `test` is set
console.log(typeof a.getTest().print); // function
or
require('./file_b'); // module from `file_a` loaded to cache and `test` is set
var a = require('./file_a').getTest();
console.log(typeof a.print); // function

Unexpected value for 'this' in express.js instantiated controllers

'this' does not appear to refer to the instantiated budget controller object. Instead it seems to refer to the global object. Does anyone know why this is?
I've defined a budget model. Injected into the controller and I'm attempting to simply generate a random 6 char string when I hit /budgets in my app. Instead this.DEFAULT_SLUG_LENGTH is undefined and I can't figure out why.
This is a dumbed down test case illustrating the issue with 'this'. I have a similar problem when referencing the injected this.budget within another function to query the db based on the slug value.
//models/budget.js
var Schema = require('jugglingdb').Schema;
var schema = new Schema('postgres',{url:process.env.DATABASE_URL});
var Budget = schema.define('budgets',{
total: Number,
slug: String
});
module.exports = Budget;
====================
//controllers/budget.js
function BudgetController (budget) {
this.budget = budget;
};
BudgetController.prototype.DEFAULT_SLUG_LENGTH = 6;
BudgetController.prototype.generateSlug = function (req,res) {
var slug = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < this.DEFAULT_SLUG_LENGTH; i++) {
slug += possible.charAt(Math.floor(Math.random() * possible.length));
}
res.send(slug);
};
module.exports = BudgetController;
===================
//app.js
var express = require('express');
var app = express();
app.use(express.bodyParser());
// models
var Budget = require('./models/budget');
// controllers
var BudgetController = require('./controllers/budget');
var budgetCtrl = new BudgetController(Budget);
// routes
app.get('/budgets',budgetCtrl.generateSlug);
app.listen(process.env.PORT || 4730);
If I manually instantiate the model/controller in the node repl, the generateSlug method works fine. If I restructure my code so that the BudgetController is a function that returns an object {} with methods, that seems to work fine. Is there some issue with my use of prototype/new ?
express takes functions and invokes them without a preceding object, so if you want to use an object method bound to a specific this as an express route handler function, you need to bind it:
app.get('/budgets', budgetCtrl.generateSlug.bind(budgetCtrl));

binding values while looping in constructor CoffeeScript

I have decided to use CoffeeScript and i have been trying to convert Node.js module of mine to CoffeeScript. So, here is the code in JS:
function Domain(obj){
var self = this;
for (var key in obj){
this[key] = obj[key]; //merge values
}
}
Domain.prototype.save = function(fn){
}
And my attempt to have the same in the CoffeeScript as following:
class Domain
consructor: (obj) ->
for own key, value of obj
#key = value
save: (fn) ->
module.exports = Domain
The following test fails:
require('coffee-script');
var should = require('should')
, Domain = require('../index');
should.exist(Domain);
var domain = new Domain({'attOne':1, 'attTwo':2});
domain.should.have.property('save');
domain.should.have.property('attOne');
domain.should.have.property('attTwo');
domain.save.should.be.an.instanceof(Function);
console.log('All tests passed');
The property 'attrOne' and 'attrTwo' is not binding to the Domain class. I have compiled the coffee code by 'coffee -c index.coffee' to see the js code:
(function() {
var Domain,
__hasProp = {}.hasOwnProperty;
Domain = (function() {
function Domain() {}
Domain.prototype.consructor = function(obj) {
var key, value, _results;
_results = [];
for (key in obj) {
if (!__hasProp.call(obj, key)) continue;
value = obj[key];
_results.push(this.key = value);
}
return _results;
};
Domain.prototype.save = function(fn) {};
return Domain;
})();
module.exports = Domain;
}).call(this);
From the compiled js, i see the '_result' array being generated and returned but never binded to 'this'. How would i bind the array into the class scope to pass the test?Thank You
Your line of CoffeeScript
#key = value
is not equivalent to the javascript you want
this[key] = obj[key];
The way # works is that it is simply replaced by either this or this.. Thus, #key compiles to this.key. You should instead use
#[key] = value
Additionally, you spelled constructor wrong (as consructor). In CoffeeScript, constructor is a special method that compiles differently from a normal method; if it is spelled wrong, CoffeeScript assumes you want an empty one.
consructor: (obj) ->
Domain.prototype.consructor
You are missing a t.
(I keep saying that)

Resources