Using Node, Sinon, Chai, proxyquire for fetch, and Mocha
How come this sinon spy assertion fooCallback1.should.have.been.called; is failing to be called once? I see with console.log(fooCallback1) in the source code that the callCount is 1.
This is the first and only test...so I don't see a reason to reset the spy.
function setLight(...args) {
var request;
var lightNumber;
var alertName;
var callback1;
var callback2;
var callback3;
[request, lightNumber, alertName,
callback1, callback2, callback3] = args;
return fetch(request)
.then(status)
.then(toJSON)
.then(() => {
if(Boolean(callback1)) {
console.log('one')
callback1(lightNumber);
console.log(callback1);
}
before(()=> {
fetch = sinon.stub().returnsPromise();
var response = {
status: 200,
json: () => { 'foo' }
};
fetch.resolves(response);
fetchHelper = proxy('../lib/fetch-helper', {'node-fetch': fetch});
});
it('should run fetch for light effect and shutoff', (done)=> {
var fooCallback1 = sinon.spy();
fetchHelper.setLight('foo', 123, 'foo-alert', fooCallback1);
fetch.should.have.been.called;
fooCallback1.should.have.been.called;
done();
});
1) when executing setLight should run fetch for light effect and shutoff:
AssertionError: expected spy to have been called at least once, but it was never called
at Context.it (test/fetch-helper.js:24:34)
when executing setLight
1) should run fetch for light effect and shutoff
one
{ [Function: proxy]
isSinonProxy: true,
formatters:
{ c: [Function: c],
n: [Function: n],
D: [Function: D],
C: [Function: C],
t: [Function: t],
'*': [Function: *] },
reset: [Function: reset],
invoke: [Function: invoke],
named: [Function: named],
getCall: [Function: getCall],
getCalls: [Function: getCalls],
calledBefore: [Function: calledBefore],
calledAfter: [Function: calledAfter],
calledImmediatelyBefore: [Function: calledImmediatelyBefore],
calledImmediatelyAfter: [Function: calledImmediatelyAfter],
withArgs: [Function: withArgs],
matches: [Function: matches],
printf: [Function: printf],
calledOn: [Function],
alwaysCalledOn: [Function],
calledWith: [Function],
calledWithMatch: [Function],
alwaysCalledWith: [Function],
alwaysCalledWithMatch: [Function],
calledWithExactly: [Function],
alwaysCalledWithExactly: [Function],
neverCalledWith: [Function],
neverCalledWithMatch: [Function],
threw: [Function],
alwaysThrew: [Function],
returned: [Function],
alwaysReturned: [Function],
calledWithNew: [Function],
alwaysCalledWithNew: [Function],
callArg: [Function],
callArgWith: [Function],
callArgOn: [Function],
callArgOnWith: [Function],
yield: [Function],
invokeCallback: [Function],
yieldOn: [Function],
yieldTo: [Function],
yieldToOn: [Function],
spyCall: { [Function: createSpyCall] toString: [Function: toString] },
called: true,
notCalled: false,
calledOnce: true,
calledTwice: false,
calledThrice: false,
callCount: 1,
firstCall:
{ proxy: [Circular],
thisValue: undefined,
args: [ 123 ],
returnValue: undefined,
exception: undefined,
callId: 11,
errorWithCallStack:
Error
at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
at process._tickCallback (internal/process/next_tick.js:103:7) },
secondCall: null,
thirdCall: null,
lastCall:
{ proxy: [Circular],
thisValue: undefined,
args: [ 123 ],
returnValue: undefined,
exception: undefined,
callId: 11,
errorWithCallStack:
Error
at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
at process._tickCallback (internal/process/next_tick.js:103:7) },
args: [ [ 123 ] ],
returnValues: [ undefined ],
thisValues: [ undefined ],
exceptions: [ undefined ],
callIds: [ 11 ],
errorsWithCallStack:
[ Error
at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
at process._tickCallback (internal/process/next_tick.js:103:7) ],
displayName: 'spy',
toString: [Function: toString],
instantiateFake: [Function: create],
id: 'spy#11' }
- should set normal alert lock on
- should set normal alert lock off after time
0 passing (15ms)
2 pending
1 failing
1) when executing setLight should run fetch for light effect and shutoff:
AssertionError: expected spy to have been called at least once, but it was never called
at Context.it (test/fetch-helper.js:27:34)
Here is another way I wrote the test and still same result:
before((done)=> {
fetch = sinon.stub().returnsPromise();
fetchHelper = proxy('../lib/fetch-helper', {'node-fetch': fetch});
done()
});
after(()=> {
});
it('should run fetch for light effect and shutoff', (done)=> {
function fooCallback1() { console.log('aaaaaaaaaaaaaaaaaaaa') }
var foo = sinon.spy(fooCallback1)
fetchHelper.setLight('foo', 123, 'foo-alert', fooCallback1);
var response = {
status: 200,
json: () => { 'foo' }
};
fetch.resolves(response);
fetch.should.have.been.called;
foo.should.have.been.called;
done();
});
when executing setLight
one
aaaaaaaaaaaaaaaaaaaa
1) should run fetch for light effect and shutoff
- should set normal alert lock on
- should set normal alert lock off after time
0 passing (10ms)
2 pending
1 failing
1) when executing setLight should run fetch for light effect and shutoff:
AssertionError: expected fooCallback1 to have been called at least once, but it was never called
at Context.it (test/fetch-helper.js:28:25
fetchHelper.setLight() is asynchronous, but your test isn't waiting for it to complete, so the order in which the code is run is something like this:
fetchHelper.setLight()
fetch(request)
fetch.should.have.been.called
fooCallback1.should.have.been.called
done()
console.log('one')
callback1(lightNumber)
console.log(callback1)
You need to rewrite the test so it will wait for the callback to get called. You don't use a spy for that, but a regular callback function in which you test the assertion(s):
it('should run fetch for light effect and shutoff', done => {
fetchHelper.setLight('foo', 123, 'foo-alert', () => {
fetch.should.have.been.called;
done();
});
});
In addition to robertklep's excellent answer, here is a alternate way I got it to work. I call the fetch resolve after calling the fetch wrapper setLight:
it('should run fetch for light effect and shutoff', (done)=> {
var foo = sinon.spy()
fetchHelper.setLight('foo', 123, 'foo-alert', foo);
fetch.resolves(response);
fetch.should.have.been.called;
foo.should.have.been.called;
done();
});
Related
I am testing some nodejs classes and using the node modules dir for details. But it would be easier to inspect. Below is the example. It does not display all the methods. Is just states -
'[class Wallet]'
Is there a way to show the methods and more class details?
~/node_modules$ node
Welcome to Node.js v12.18.3.
Type ".help" for more information.
> const x=require('fabric-network')
undefined
> x
{
Gateway: [class Gateway],
Wallet: [class Wallet],
Wallets: [class Wallets],
IdentityProviderRegistry: [class IdentityProviderRegistry],
HsmX509Provider: [class HsmX509Provider],
DefaultCheckpointers: [class DefaultCheckpointers],
DefaultEventHandlerStrategies: {
MSPID_SCOPE_ALLFORTX: [Function],
MSPID_SCOPE_ANYFORTX: [Function],
NETWORK_SCOPE_ALLFORTX: [Function],
NETWORK_SCOPE_ANYFORTX: [Function],
NONE: [Function]
},
DefaultQueryHandlerStrategies: {
MSPID_SCOPE_SINGLE: [Function],
MSPID_SCOPE_ROUND_ROBIN: [Function]
},
TimeoutError: [class TimeoutError extends FabricError]
}
> x.Wallet
[class Wallet]
> util.inspect(x.Wallet)
'[class Wallet]'
You can use util module and inspect method to dive into an object methods and classes. Here I set showHidden to true, depth to 1 and color to true.If depth sets to null will cause it to recurse 'all the way', showing every level. It will return colorful string as following.
const x = require('fabric-network');
const util = require('util');
console.log(util.inspect(x, true, 10, true))
output
{
Gateway: [Function: Gateway] {
[length]: 0,
[prototype]: [Gateway],
[name]: 'Gateway'
},
Wallet: [Function: Wallet] {
[length]: 1,
[prototype]: [Wallet],
[name]: 'Wallet'
},
Wallets: [Function: Wallets] {
[length]: 0,
[prototype]: [Wallets],
[newInMemoryWallet]: [AsyncFunction],
[newFileSystemWallet]: [AsyncFunction],
[newCouchDBWallet]: [AsyncFunction],
[name]: 'Wallets'
},
IdentityProviderRegistry: [Function: IdentityProviderRegistry] {
[length]: 0,
[prototype]: [IdentityProviderRegistry],
[name]: 'IdentityProviderRegistry'
},
HsmX509Provider: [Function: HsmX509Provider] {
[length]: 0,
[prototype]: [HsmX509Provider],
[name]: 'HsmX509Provider'
},
DefaultCheckpointers: [Function: DefaultCheckpointers] {
[length]: 0,
[prototype]: [DefaultCheckpointers],
[file]: [AsyncFunction],
[name]: 'DefaultCheckpointers'
},
DefaultEventHandlerStrategies: {
[__esModule]: true,
MSPID_SCOPE_ALLFORTX: [Function],
MSPID_SCOPE_ANYFORTX: [Function],
NETWORK_SCOPE_ALLFORTX: [Function],
NETWORK_SCOPE_ANYFORTX: [Function],
NONE: [Function]
},
DefaultQueryHandlerStrategies: {
[__esModule]: true,
MSPID_SCOPE_SINGLE: [Function],
MSPID_SCOPE_ROUND_ROBIN: [Function]
},
TimeoutError: [Function: TimeoutError] {
[length]: 1,
[prototype]: [TimeoutError],
[name]: 'TimeoutError',
stackTraceLimit: 10
}
}
I have looked at How to test getDerivedStateFromProps with Jest and Enzyme but it is not working for me. here is my test
it('should be red processing only, routing, security grey while bg tasks are running', () => {
component = mount(
<ProcessingStatus store={store}/>
);
const instance = component.instance();
//console.log(instance)
component.setProps({ processing_status: {
header:{
error: true,
message: 'This comms matrix is currently processing flows',
statusCode: 200
},
body: {}
} });
console.log(component.state())
console.log(component.props())
expect(component.find(TrafficLight).length).toEqual(3);
expect(component.find(TrafficLight).at(0).props().RedOn).toEqual(true);
expect(component.find(TrafficLight).at(0).props().AmberOn).toEqual(false);
expect(component.find(TrafficLight).at(0).props().GreenOn).toEqual(false);
});
component.state() or instance.state is always empty {}.
This is the contents of component.props()
{ store:
{ getState: [Function: getState],
getActions: [Function: getActions],
dispatch:
{ [Function: mockConstructor]
_isMockFunction: true,
getMockImplementation: [Function],
mock: [Getter/Setter],
mockClear: [Function],
mockReset: [Function],
mockRestore: [Function],
mockReturnValueOnce: [Function],
mockResolvedValueOnce: [Function],
mockRejectedValueOnce: [Function],
mockReturnValue: [Function],
mockResolvedValue: [Function],
mockRejectedValue: [Function],
mockImplementationOnce: [Function],
mockImplementation: [Function],
mockReturnThis: [Function],
mockName: [Function],
getMockName: [Function] },
clearActions: [Function: clearActions],
subscribe: [Function: subscribe],
replaceReducer: [Function: replaceReducer] },
processing_status:
{ header:
{ error: true,
message: 'This comms matrix is currently processing flows',
statusCode: 200 },
body: {} } }
I need this to be triggered as depending on my props values the state changes and renders other conditions.
If it is a connected component it needs to be retrieved differently.
component = mount(
<ProcessingStatus store={store}/>
);
const instance = component.find('ProcessingStatus').instance();
component.setProps({ processing_status: {
header:{
error: true,
message: 'This comms matrix is currently processing flows',
statusCode: 200
},
body: {}
} });
console.log(instance.state);
Provided me with
console.log tests/jest/components/common/ProcessingStatus/index.test.js:122
{ nextStep: 'This comms matrix is currently processing flows' }
What is not clear in previous answers is if there are connected or not and if shallow or mount is being used
I am having a little trouble with retrieving the data from a table using knex select.
I would like to do something like this
function getUserData(userId){
let user = knex.select('xp','money','rolls','twenty').from('users').where('user_id', userId);
return user;
}
user = getUserData(userId);
user.xp; // do something with this value
but this outputs the following (if i console.log it), but not the requested info from the Select query, unless i am just not sure how to retrieve it:
Builder {
client:
Client_MySQL {
config: { client: 'mysql', connection: [Object] },
connectionSettings:
{ host: '127.0.0.1',
user: 'XXXXXXXXXX',
password: 'XXXXXXXXXX',
database: 'XXXXXXXXXX' },
driver:
{ createConnection: [Function: createConnection],
createPool: [Function: createPool],
createPoolCluster: [Function: createPoolCluster],
createQuery: [Function: createQuery],
escape: [Function: escape],
escapeId: [Function: escapeId],
format: [Function: format],
raw: [Function: raw] },
pool:
Pool {
creator: [Function: create],
destroyer: [Function: destroy],
validate: [Function: validate],
log: [Function],
acquireTimeoutMillis: 60000,
createTimeoutMillis: 30000,
idleTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createRetryIntervalMillis: 200,
propagateCreateError: true,
min: 2,
max: 10,
used: [],
free: [],
pendingCreates: [],
pendingAcquires: [],
destroyed: false,
interval: null },
valueForUndefined:
Raw {
client: [Circular],
sql: 'DEFAULT',
bindings: undefined,
_wrappedBefore: undefined,
_wrappedAfter: undefined,
_debug: undefined },
_events:
{ start: [Function],
query: [Function],
'query-error': [Function],
'query-response': [Function] },
_eventsCount: 4,
makeKnex: [Function: makeKnex] },
and: [Circular],
_single: { table: 'users', only: false },
_statements:
[ { grouping: 'columns', value: [Array] },
{ grouping: 'where',
type: 'whereBasic',
column: 'user_id',
operator: '=',
value: '341007826375802900',
not: false,
bool: 'and' } ],
_method: 'select',
_debug: undefined,
_joinFlag: 'inner',
_boolFlag: 'and',
_notFlag: false }
I'll write some more words here, as it requires me to do so, since it is mostly code. I hope this will be enough words.
The query run asynchronous, so you need to explicitly wait for it to finish. One way to do this is using promises:
knex.select('xp','money','rolls','twenty').from('users').where('user_id', userId)
.then(data => console.log(data));
Also make sure that the connection with the database is already established.
After config database just called an async function it will worked
const knex = require('knex')({
client: 'sqlite3',
connection: {
filename: './mydb.db',
},
useNullAsDefault: true
});
(async function(){
const posts = await knex('data');
console.log(posts)
})()
In this code i am using this sql query
SELECT * FROM data
I have a code where I am getting a duplex stream and in that function I call a callback which return me the values from redis.
function (index, arr, null, callback) {
const streamObject = stream;
const Id = arr[index].split(':')[0];
const Version = arr[index].split(':')[1];
console.log("STREAM RECEIVED IN SECOND 1");
console.log(streamObject);//printing for the first time
var response = this.ts.service(Id, Version, streamObject, (fn, type) => {
console.log("STREAM RECEIVED IN SECOND 2");
console.log(streamObject);
}
here when I print the stream object for the first time I get the stream object as follows
STREAM RECEIVED IN SECOND 1
Stream {
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
_events:
{ end: [Function],
data: [Function],
drain: [Function: ondrain],
error: [Function: onerror],
close: [Function: cleanup] },
_eventsCount: 5,
_maxListeners: undefined,
writable: true,
readable: true,
paused: false,
autoDestroy: true,
write: [Function],
push: [Function],
queue: [Function],
end: [Function],
destroy: [Function],
pause: [Function],
resume: [Function] }
and in the second time I get
STREAM RECEIVED IN SECOND 2
Stream {
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
_events: { end: [Function], data: [Function] },
_eventsCount: 2,
_maxListeners: undefined,
writable: false,
readable: false,
paused: false,
autoDestroy: true,
write: [Function],
push: [Function],
queue: [Function],
end: [Function],
destroy: [Function],
pause: [Function],
resume: [Function],
root: null }
so stream is getting modified I am not sure why is it happening, I am not doing anything with the stream in the function called.
this is how my service method look which I am calling
service(id, vn, requestObject, callback) {
log.info("entering transformer service");
var transformerCombinedKey = id + vn;
if (!_.isString(transformerId)) {
throw new TypeError("Expected a string for transformerId");
}
if (!(transformerCombinedKey in this.functionRepositoryStore)) {
//FIXME: after using cache as a implementation should ret
var persistenceClientPromise = this.persistenceClient.getTransformerByIdAndVersion(transformerId, versionNumber)
persistenceClientPromise.then(
(aJSONStringifiedTransformer) => {
if (!aJSONStringifiedTransformer) {
callback(new Error("The given transformerId, " + transformerId + ", was not found."));
return;
}
this.functionRepositoryStore[transformerCombinedKey] = JSON.parse(aJSONStringifiedTransformer).transformerFunction;
this.transformerTypeRepository[transformerCombinedKey] = JSON.parse(aJSONStringifiedTransformer).transformerType;
const code = "var _ = require('lodash'); console.log('runnig VM1'); module.exports = " + this.functionRepositoryStore[transformerCombinedKey] + ';';
var transformerFunction = vm.runInNewContext(code, sandbox);
console.log("Calling callback inside the transformer service===1 ");
console.log(JSON.stringify(transformerFunction));
callback(transformerFunction, this.transformerTypeRepository[transformerCombinedKey]);
return
},
(error) => {
log.error("Error while getting transformer for Id " + transformerId + " and versionNumber " + versionNumber);
callback(error, null);
});
} else {
const code = "var _ = require('lodash'); console.log('runnig VM2'); module.exports = " + this.functionRepositoryStore[transformerCombinedKey] + '; ';
var transformerFunction = vm.runInNewContext(code, sandbox);
console.log("Calling callback inside the transformer service=== ");
console.log(JSON.stringify(transformerFunction));
callback(transformerFunction, this.transformerTypeRepository[transformerCombinedKey]);
}
}
}
this issue I see when I hit my app for the first time, when I hit it again without restarting the app, It works fine stream remain duplex also.
and also if I remove my call to redis this method , then stream doesn't change it works fine.
I am using waterfall and this is the second function of my waterfall model like
async.waterfall([func1,func2], function (err, result) {
if (err) {
console.log(err);
reject(err);
} else {
fulfill(result);
}
});
this first function also do the same thing but it works fine, the output of first is passed to the second.
this is how I create my stream
let streamParser = JSONStream.parse('*');
streamParser.on('data', fn);
let toPassStreamObject = streamObject.pipe(JSONStream.stringify())
.pipe(streamParser)
streamObject is the stream which I get from my DB.
fn (data) {
data['Date'] = data["month"];
delete data['month'];
}
I stuck on this for some time.how to prevent stream from changing.
this is reference to my previous post
I'm having a problem getting collection from the database in my Node.js application. I'm using Mongodb 3.6.
That's how I set it up:
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
/*
===========================================================================
DB setup
===========================================================================
*/
var state = {
db: null,
}
function get() {
return state.db;
}
exports.connect_database = function(done) {
if (state.db) return done()
MongoClient.connect(process.env.DATABASE_URL, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
/* some other functions ... */
exports.return_collection = function(collection_name, callback) {
var result_array = [];
var collection = get().getCollection(collection_name);
var result = collection.find()
result.forEach(function(res) {
result_array.push(res);
}, function(error) {
console.log("error: ")
console.log(error);
if (!error)
callback(result_array);
});
}
In the main file I do:
'use strict';
// LIB IMPORTS
var env = require('node-env-file');
env(__dirname + '/.env');
// LOCAL IMPORTS
var aux = require('./modules/aux.js');
var scheduler = require('./modules/scheduler.js');
var Bot = require('./modules/bot.js');
/*
===========================================================================
DB setup
===========================================================================
*/
aux.connect_database((err) => {
if (err) {
console.log('Unable to connect to Mongo.')
process.exit(1)
} else {
console.log('Connected to db.');
}
})
I can see in the log the Connected to db. prompt, so the connection works fine. After that I try to call some function to add/retrieve data from the db and i get the error:
TypeError: get(...).getCollection is not a function
at Object.exports.return_collection
If I try to print the state.db variable I get the following result:
MongoClient {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
s:
{ url: 'mongodb://localhost:27017/BotDb',
options:
{ socketOptions: {},
read_preference_tags: null,
readPreference: [Object],
dbName: 'slackBotDb',
servers: [Object],
server_options: [Object],
db_options: [Object],
rs_options: [Object],
mongos_options: [Object],
socketTimeoutMS: 360000,
connectTimeoutMS: 30000,
promiseLibrary: [Function: Promise] },
promiseLibrary: [Function: Promise],
dbCache: {},
sessions: [] },
topology:
Server {
domain: null,
_events:
{ serverOpening: [Function],
serverDescriptionChanged: [Function],
serverHeartbeatStarted: [Function],
serverHeartbeatSucceeded: [Function],
serverHeartbeatFailed: [Function],
serverClosed: [Function],
topologyOpening: [Function],
topologyClosed: [Function],
topologyDescriptionChanged: [Function],
joined: [Function],
left: [Function],
ping: [Function],
ha: [Function],
authenticated: [Function],
error: [Function],
timeout: [Function],
close: [Function],
parseError: [Function],
open: [Object],
fullsetup: [Object],
all: [Object],
reconnect: [Function] },
_eventsCount: 22,
_maxListeners: undefined,
clientInfo:
{ driver: [Object],
os: [Object],
platform: 'Node.js v7.10.0, LE' },
s:
{ coreTopology: [Object],
sCapabilities: null,
clonedOptions: [Object],
reconnect: true,
emitError: true,
poolSize: 5,
storeOptions: [Object],
store: [Object],
host: 'localhost',
port: 27017,
options: [Object],
sessionPool: [Object],
promiseLibrary: [Function: Promise] } } }
What am I missing? In the mongo console everything looks fine:
> db.getCollection("users");
BotDb.users
I can't find any function named getCollection in the API documentation for the Node.js MongoDB native driver. Collections are usually fetched with collection('mycoll'). So you can rewrite this line:
var collection = get().getCollection(collection_name);
to
var collection = get().collection(collection_name);
Note that if you use v3.0 or later of the driver you have to modify the connect function as well. There were some changes done to the connection functions (see here). The callback now returns a client object rather than the db object. So you'll have to change your function to:
exports.connect_database = function(done) {
if (state.db) return done()
MongoClient.connect(process.env.DATABASE_URL, function(err, client) {
if (err) return done(err);
state.db = client.db('database_name');
done();
})
}
Note the 'database_name' string. It should be the name of your database.