Mocha complains about done() even though it is used - node.js

I am writing some tests of Solidity using Mocha module. The test fails below with this error despite the fact that the done() function is called and the promise is resolved (the commented out console.log() statements show that the Promise from the included module compile.js indeed resolves).
Perhaps I'm not interpreting the error correctly? I am new to Node.js, so my apologies if I cooked up a mess.
"before each" hook for "Deploy a contract":
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
let accounts;
let inbox;
beforeEach(async (done) => {
// Get a list of all accounts
accounts = await web3.eth.getAccounts();
// console.log(accounts);
const generate = require('../compile');
await generate()
.then(async data => {
var interface = data.interface;
var bytecode = data.bytecode;
// console.log('ABI ' + interface);
// console.log('BIN ' + bytecode);
inbox = await new web3.eth.Contract(JSON.parse(interface))
.deploy({data: bytecode, arguments: ['Greetings!']})
.send({from: accounts[0], gas: '1000000'});
});
done();
});
describe('Inbox testing', () => {
it('Deploy a contract', () => {
console.log('Contract ' + inbox);
});
});
The function generate() imported from compile.js returns promise
function generate() {
return new Promise((resolve, reject) => {
...
})
})
}
module.exports = generate;

You cannot use a done callback with an async function in Mocha. Also, it's not a good idea to pass an async function to .then. I would refactor the test function to use asynchronous style code only.
beforeEach(async () => {
// Get a list of all accounts
const accounts = await web3.eth.getAccounts();
// console.log(accounts);
const generate = require('../compile');
const data = await generate();
var interface = data.interface;
var bytecode = data.bytecode;
// console.log('ABI ' + interface);
// console.log('BIN ' + bytecode);
inbox = await new web3.eth.Contract(JSON.parse(interface))
.deploy({data: bytecode, arguments: ['Greetings!']})
.send({from: accounts[0], gas: '1000000'});
});

I think mocha might be going crazy because you need to manually close your web3 connection after running your tests. Try calling disconnect after your tests run:
after(done => {
web3.currentProvider.disconnect()
done();
}

Related

Sinon.restore not working for stubbing and testing AWS functions

So I'm trying to write a few tests for testing an AWS wrapper library that I have been writing.
The tests are running individually without any issues, but won't all run as one 'describe' block.
const AWS_REGION = 'eu-west-2';
const aws = require('aws-sdk');
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
chai.use(sinonChai);
// These help:
// https://stackoverflow.com/questions/26243647/sinon-stub-in-node-with-aws-sdk
// https://stackoverflow.com/questions/61516053/sinon-stub-for-lambda-using-promises
describe('SQS Utilities Test', () => {
afterEach(() => {
sinon.restore();
});
it('should add to SQS', async () => {
sinon.stub(aws.config, 'update');
const sqs = {
sendMessage: sinon.stub().returnsThis(),
promise: sinon.stub()
};
sinon.stub(aws, 'SQS').callsFake(() => sqs);
// these use the above stubbed version of aws
const AWSUtilities = require('../index').AWSUtilities;
const awsUtilities = new AWSUtilities(AWS_REGION);
const response = await awsUtilities.postToSQS('https://example.com', { id: 1}, 'chicken');
expect(sqs.sendMessage).to.have.been.calledOnce;
});
it('should get from SQS', async () => {
sinon.stub(aws.config, 'update');
const sqs = {
receiveMessage: sinon.stub().returnsThis(),
promise: sinon.stub()
};
sinon.stub(aws, 'SQS').callsFake(() => sqs);
// these use the above stubbed version of aws
const AWSUtilities = require('../index').AWSUtilities;
const awsUtilities = new AWSUtilities(AWS_REGION);
const response = await awsUtilities.getFromSQS('https://example.com');
expect(sqs.receiveMessage).to.have.been.calledOnce;
});
...
What I noticed, is that in the second test, the error I am getting is sqs.receiveMessage is not a function, which means that the second test is using the sqs object from the first test (I can further verify this as the error changes if I add receiveMessage to the first test sqs object).
Is this a bug in sinon restore, or have I written something incorrectly? Here is the whole library: https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js
This is not an issue with Sinon. This an issue of how you are stubbing AWS SDK. Let's break down what's happening within the code you have shared.
const sqs = {
sendMessage: sinon.stub().returnsThis(),
promise: sinon.stub()
};
sinon.stub(aws, 'SQS').callsFake(() => sqs);
// these use the above stubbed version of aws
const AWSUtilities = require('../index').AWSUtilities;
This code does the following
Stub SQS of aws.
Load AWSUtilities.js (based on the source code in github)
AWSUtilities.js does the following as soon as its loaded
const aws = require('aws-sdk');
const sqs = new aws.SQS();
// code removed to demo the concept
The above code creates an internal sqs object, which in this case is made using the stubbed aws module. In node once a module is loaded using require it's cached in memory i.e the above code executes only once.
So when the first it() executes it in turn loads AWSUtilities.js for the first time and is cached. Any subsequent calls gets the cached version. When you call sinon.restore it only restores the SQS function of aws module it doesn't restore the sqs object that was created within AWSUtilities.js.
I hope that explains the reason for the behavior that you are seeing.
There are multiple ways to fix this issue. Dependency injection, using modules like proxyquire, rewire, stubbing aws from a central location before all test cases etc.
The following is an option to fix it in just the test cases shown here.
describe('SQS Utilities Test', () => {
let AWSUtilities, sqsStub;
before(() => {
sinon.stub(aws.config, 'update');
sqsStub = {
sendMessage: sinon.stub().returnsThis(),
receiveMessage: sinon.stub().returnsThis(),
promise: sinon.stub()
};
sinon.stub(aws, 'SQS').callsFake(() => sqs);
AWSUtilities = require('../index').AWSUtilities;
});
after(() => {
sinon.restore();
});
it('should add to SQS', async () => {
const awsUtilities = new AWSUtilities(AWS_REGION);
const response = await awsUtilities.postToSQS('https://example.com', { id: 1}, 'chicken');
expect(sqsStub.sendMessage).to.have.been.calledOnce;
});
it('should get from SQS', async () => {
const awsUtilities = new AWSUtilities(AWS_REGION);
const response = await awsUtilities.getFromSQS('https://example.com');
expect(sqsStub.receiveMessage).to.have.been.calledOnce;
});
});

Mocha times out calling async promise chain in Before hook despite using done

I'm running suite of async integration tests into a mongoose db using mocha and chai on node.js. Most are running fine but for one I have to carry out some pre-test db-prep with the before hook. When I use Done in the before hook Mocha times out.
Error I'm getting is "Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/donfelipe/Sites/Agents/test/agents.js)"
Have tried moving the done() into a finally block at the end of the promise chain, but that just results in the the it block running before the async chain has finished executing. Kind of stumped.
/*
test case to check that the get function returns
*/
process.env.NODE_ENV = 'test-db';
'use strict'
const mongoose = require("mongoose");
const schemas = require('../app_api/models/schemas');
const agents = require('../app_api/controllers/agents.js');
const lists = require('../app_api/controllers/lists.js');
const server = require('../app.js');
const assert = require('assert')
const config = require('config')
//Require the dev-dependencies
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
chai.use(chaiHttp);
describe('Agents test Suite', () => {
beforeEach(() => {
//don't have anything too do in this case
})
describe('/GET listAddItem suite', function () {
//before we can test deletion we have to add at least one list in.
const userModel = mongoose.model('User', schemas.userSchema);
const agentModel = mongoose.model('Agent', schemas.agentSchema);
let agentToAddId = {}
const listObject = {
listName: 'testList',
_id: mongoose.Types.ObjectId(),
agents: [{
_id: "00000000000000000000000",
name: "Test agent"
}]
}
const lengthToTestAgainst = listObject.agents.length
beforeEach((done) => {
userModel.findById(config.defaultUserId)
.select('agentList')
.then((parentDoc) => {
if (!parentDoc) {
console.error('Unable to find user');
}
parentDoc.agentList.push(listObject)
return parentDoc.save()
})
.then(() => {
return agentModel.find({})
})
.then((response) => {
agentToAddId = response[0]._id
//console.log(response[0]._id);
done()
})
})
it('should add a new item into the testList', (done) => {
chai.request(server)
.get(`/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}`)
.end((err, response) => {
response.should.have.status(200)
response.body.agentList.testList.should.not.be.equal(lengthToTestAgainst + 1)
done(err)
})
})
})
})
Duh. Resolved this myself. A real case of the tail wagging the dog.
So I mocked up the call to the API that the chai.request(server) is making:
/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}
and submitted it through POSTMAN. Turns out there was a bug in the API and so it was not returning a response, so the timeout I was getting from Mocha was a valid response, the request was just hanging.

Unit test with sinon fake does not resolve promise

I'm learning nodejs and wrote this wrapper for a shelljs function, which in practice seems to work as intended.
/**
* Wrapper for Shelljs.exec to always return a promise
*
* #param {String} cmd - bash-compliant command string
* #param {String} path - working directory of the process
* #param {Object} _shell - alternative exec function for testing.
* #returns {String}
* #throws {TypeError}
*/
function shellExec(cmd, path, _shell = shelljs){
if( typeof _shell.exec !== "function") throw new TypeError('_shell.exec must be a function');
return new Promise((resolve, reject) => {
let options = { cwd: path, silent: true, asyc: true }
// eslint-disable-next-line no-unused-vars
return _shell.exec(cmd, options, (code, stdout, stderr) => {
// shelljs.exec does not always return a code
if(stderr) {
return reject(stderr);
}
return resolve(stdout);
});
});
}
However when I attempt to unit test it, the function times out. I have read the mochajs docs about async code, promises or async/await in tests. I want to use a sinon fake that returns a promise which I know works. Mocha tells me the error is that the function is not returning a promise via the error Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. I imagine I have constructed the fake improperly but I cannot see how else I should have done this.
const { expect, use } = require('chai');
const sinon = require('sinon');
const sinonChai = require("sinon-chai");
const utils = require('../utility/exec');
use(sinonChai);
it('sinon fake should resolve', async () =>{
const fake = sinon.fake.resolves('resolved');
const result = await fake();
expect(result).to.equal('resolved');
});
describe('Utility Functions', () =>{
describe('shellExec', () =>{
it('should accept an alternate execute function', async () =>{
const fakeShell = { exec: sinon.fake.resolves('pass') };
const result = await utils.shellExec('pwd', 'xyz', fakeShell);
expect(result).to.equal('pass');
expect(fakeShell.exec).to.have.been.calledOnce;
});
});
});
You function is a little complex but nothing sinon can't handle with stubs. See https://sinonjs.org/releases/v1.17.7/stubs/ for more info but what you should use is callsArgOnWith before the function.
Instead of setting exec to return a promise you need to set it as a stub. This way you can call the callback using the callsArgOnWith function when it is encountered.
I've changed your test so it now passes by changing the fake exec function to return a stub const fakeShell = { exec: sinon.stub() }; and adding the line fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null) before running your function
const { expect, use } = require('chai');
const sinon = require('sinon');
const sinonChai = require("sinon-chai");
const utils = require('./main');
use(sinonChai);
it('sinon fake should resolve', async () =>{
const fake = sinon.fake.resolves('resolved');
const result = await fake();
expect(result).to.equal('resolved');
});
describe('Utility Functions', () =>{
describe('shellExec', () =>{
it('should accept an alternate execute function', async () =>{
const fakeShell = { exec: sinon.stub() };
fakeShell.exec.callsArgOnWith(2, null, null, 'pass', null)
const result = await utils.shellExec('pwd', 'xyz', fakeShell);
expect(result).to.equal('pass');
expect(fakeShell.exec).to.have.been.calledOnce;
});
});
});
your _shell.exec is just a callback function, It's not a Promise. That's why when you fake shell.exec to be a promise, your resolve will never been called. I think you need to fake your fakeShell to something like this:
const fakeShell = {
exec: (cmd, options, cb) => {
cb(true, 'pass', null);
}
};

Mocha With Selenium The Error: TypeError: Wait condition must be a promise-like object, function, or a Condition object

I'm new to Mocha and Selenium for testing an Express application. I have the following simple code, but I can't figure out what's going wrong.
describe("authenticateWithGoogle", function() {
it("return a valid access token for our tests", function() {
return new Promise( function(resolve) {
var driver = new Builder().forBrowser('chrome').build();
driver.get('https://www.google.com');
driver.wait("Google",5000).then( (quitDriver,handleFailure) => {
console.log("wait over");
assert.ok(true);
});
resolve();
}).then();
});
});
I receive the follow error when I run 'mocha':
TypeError: Wait condition must be a promise-like object, function, or a Condition object
This occurs on the 'driver.wait' line in the code above. I really don't understand what the errors means.
I tried same selenium-webdriver 4.0.0 alpha.1 and it worked.
Based on its example, it uses async await so I use same way.
const {Builder, By, Key, until} = require('selenium-webdriver');
const chai = require('chai');
const assert = chai.assert;
describe("authenticateWithGoogle", function() {
it("return a valid access token for our tests", async function() {
this.timeout(5000);
let driver = await new Builder()
.usingServer('http://localhost:4444/wd/hub')
.forBrowser('chrome').build();
try {
await driver.get('http://www.google.com');
await driver.wait(until.titleIs('Google'), 1000);
} finally {
assert.ok(true);
await driver.quit();
}
});
});
My Setup:
node 8.9
selenium-standalone https://www.npmjs.com/package/selenium-standalone
Yes, for testing oauth you can create integration/e2e test for it. You're already in the right path.
Hope it helps

Using async in event emitter

I am being challenged trying to make an async call inside an event.
Here's the code from Nodemailer - I've added the line where I need to make an async call:
let transporter = nodemailer.createTransport({
SES: new aws.SES({
apiVersion: '2010-12-01'
}),
sendingRate: 1 // max 1 messages/second
});
// Push next messages to Nodemailer
transporter.on('idle', () => {
while (transporter.isIdle()) {
// I need to make an async db call to get the next email in queue
const mail = await getNextFromQueue()
transporter.sendMail(mail);
}
});
I found this post which suggest switching things around which makes sense however I have been unable to apply it correctly to this.
Update - The answer was to mock sendMail using Sinon.
You can just mark your callback as async and use await inside of it.
The fact that it's an event handler callback makes no difference since at the end it's just a plain-old Function.
Node snippet
'use strict'
const EventEmitter = require('events')
const myEmitter = new EventEmitter()
const getDogs = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(['Woof', 'Woof', 'Woof'])
}, 500)
})
}
myEmitter.on('event', async () => {
const dogs = await getDogs()
console.log(dogs)
})
myEmitter.emit('event')
Alternative scenario
If you still can't get it to work it might be because transporter.on is not the same as EventEmitter.on - meaning it's a custom function provided by transporter.
It could assume internally that the callback function provided is not a Promise - keep in mind that labelling a function as async forces the function to always implicitly return a Promise.
If that's the case you might want to wrap the async function in an IIFE.
// ..rest of code from above
myEmitter.on('event', () => {
// wrap into an IIFE to make sure that the callback
// itself is not transformed into a Promise
(async function() {
const dogs = await getDogs()
console.log(dogs)
})()
})
myEmitter.emit('event')
I had a similar scenario and if I were you I would have done the following.
let transporter = nodemailer.createTransport({
SES: new aws.SES({
apiVersion: '2010-12-01'
}),
sendingRate: 1 // max 1 messages/second
});
const sendMail = async () => {
while (transporter.isIdle()) {
// I need to make an async db call to get the next email in queue
const mail = await getNextFromQueue()
transporter.sendMail(mail);
}
}
// Push next messages to Nodemailer
transporter.on('idle', sendMail);

Resources