Mocha UnhandledPromiseRejectionWarning: AssertionError - node.js

I'm beginning start writing some Mocha tests for an application I've been working on.
When running onn of the tests fails I'm getting the following error:
(node:12988) UnhandledPromiseRejectionWarning: AssertionError: Failure reason: expected 'Fail' to include 'Success'
Whilst I understand the failure reason, I'm really struggling to resolve the UnhandledPromiseRejection warning and have gone round in circles with the issue.
Any ideas?
describe('Test', function() {
it('This should complete succesfully', function() {
class Obj {
getResult() {
return new Promise(function(resolve, reject) {
resolve('Fail');
});
}
}
var obj = new Obj();
obj.getResult().then(function(res) {
expect(res).to.include('Success');
});
});
});

The test is asynchronous and should be treated as such. Modern testing frameworks support promises, including Mocha. If there's a promise, it should be returned from test function:
return obj.getResult().then(function(res) {
expect(res).to.include('Success');
});

Related

Proper way to handle fetch errors in NodeJS v18?

I switched to NodeJS v18 with the built-in fetch and I'm using it as such:
async function get511AK() {
let res = await fetch(URL, { method: 'GET' })
if (res.ok && (res.headers.get('content-type').includes('json'))) {
let data = await res.json();
jsonresponseAK = data;
} else {
console.log("(" + res.url + ') is not json');
}
}
However, sometimes I'm getting a timeout on the URL, which is going to happen, but it's causing the script to exit. I've tried wrapping this in try/catch and it did not prevent it from exiting.
This never happened in Node v12 under the node-fetch library. What else can I add to control those connection timeouts?
node:internal/deps/undici/undici:11118
Error.captureStackTrace(err, this);
^
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Timeout.get511AK [as _onTimeout] (/home/wazebot/dot-scripts/script-relay.js:76:12) {
cause: ConnectTimeoutError: Connect Timeout Error
at onConnectTimeout (node:internal/deps/undici/undici:6625:28)
at node:internal/deps/undici/undici:6583:50
at Immediate._onImmediate (node:internal/deps/undici/undici:6614:13)
at process.processImmediate (node:internal/timers:471:21) {
code: 'UND_ERR_CONNECT_TIMEOUT'
}
}
Node.js v18.12.1
Hope it helped!
process.on('uncaughtException', console.log);
// Uncaught Exception thrown - when you throw an error and did not catch anywhere.
process.on('unhandledRejection', console.log);
// Unhandled Rejection at Promise - similar, when you fail to catch a Promise.reject.

Why doesn't Jest complete the async operation(s) in this Node test?

I have the following simple test setup:
test('what did I do to deserve this', async () => {
expect.assertions(1)
const data = await fetchData() // or fetchData2
expect(data).toBe('peanut butter')
})
async function fetchData () {
return "peanut butter"
}
async function fetchData2 () {
return knex.select('name').from('foos')
}
When I use fetchData jest finishes happily.
But when I use fetchData2 it complains of this:
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't
stopped in your tests. Consider running Jest with
--detectOpenHandles to troubleshoot this issue.
The data variable does have the result from the db query, and other callers higher in the API resolve the query fine and continue the execution of other statements.
I have tried:
the --detectOpenHandles flag but it doesn't show me anything.
making the expect pass for fetchData2 in case it was the issue described here
passing a done arg to the async function in test. It does exist, but calling it does not fix the warning.
throwing try/catch blocks at it
Thanks for any help on making this happy.
Versions of things:
Node v11.1.0
"jest": "^23.6.0"
"knex": "^0.15.2"
To forcefully close Jest rather than DB Connection :
--forceExit
So my test script looked something like this,
"scripts": {
"test": "jest --forceExit"
}
You need to call knex.destroy() in the end of the test suite to teardown connection pool.
Got these errors:
1.
A worker process has failed to exit gracefully and has been force
exited. This is likely caused by tests leaking due to improper
teardown. Try running with --detectOpenHandles to find leaks.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't
stopped in your tests. Consider running Jest with
--detectOpenHandles to troubleshoot this issue.
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block, or by rejecting a
promise which was not handled with .catch(). The promise rejected with
the reason "Error: Request failed with status code 404".] { code:
'ERR_UNHANDLED_REJECTION' }
##[error]Cmd.exe exited with code '1'.
Example with global setup and teardown:
// setup.js
const knexConfiguration = require('../config/knex')[process.env.NODE_ENV];
const knex = require('knex');
const setup = async () => {
const client = knex(knexConfiguration)
await client.migrate.latest();
await client.destroy();
};
module.exports = setup;
// teardown.js
const knexConfiguration = require('../config/knex')[process.env.NODE_ENV];
const knex = require('knex');
const teardown = async () => {
const client = knex(knexConfiguration)
await client.migrate.rollback();
await client.destroy();
};
module.exports = teardown;
Source:
https://github.com/facebook/jest/issues/7287#issuecomment-510068325
Another example:
dbConnection.js
export default new Sequelize({...}); // The Sequelize instance.
some.spec.js
import dbConnection from './dbConnection';
const { SomeModel } = dbConnection.models;
describe('...', () => {
beforeEach(async () => {
await SomeModel.create({...});
});
...
});
afterAll(async done => {
// Closing the DB connection allows Jest to exit successfully.
dbConnection.close();
done();
});
https://github.com/facebook/jest/issues/7287#issuecomment-434058564

Sourcemaps in puppeteer?

I'm puppeteering (could this be a verb?) an app built with webpack and on page.on('error') I get errors like this:
TypeError: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_promise___default.a.map is not a function
at _default.<anonymous> (....../app.js:23858:89)
at Generator.next (<anonymous>)
at step (....../app.js:35041:30)
at ....../app.js:35052:13
Is there a way, and if so how could I get these errors correctly sourcemapped?
(I know for sure I'm generating the sourcemaps)
I did it in the following way... A bit delayed but someone might find it useful.
Once you have navigated to the page throwing the error you inject this lib: https://github.com/novocaine/sourcemapped-stacktrace
await page.addScriptTag({
url: 'https://cdn.jsdelivr.net/npm/sourcemapped-stacktrace#1.1.8/dist/sourcemapped-stacktrace.js',
});
Then you listen for the error events:
page.on('pageerror', logStackTrace);
page.on('error', logStackTrace);
In the logStackTrace function you extract the sourcemapped stacktrace as follows and log it:
const logStackTrace = async (error) => {
page.evaluate(stack => new Promise(resolve =>
window.sourceMappedStackTrace.mapStackTrace(stack, (newStack) => {
resolve(newStack);
})
), typeof error.stack === 'string' ? error.stack : error.stack.join('\n'))
.then((result) => {
console.log('ERROR:', error.message, result[0]);
});
};
It only correctly maps the first line of the stack for me though. Still infinitely more useful.
If you use one of the eval variants for your webpack devtool then Puppeteer should pick it up:
// webpack.config.js
module.exports = {
// ...
mode: 'development',
devtool: 'eval-source-map',
};

How to test a function that throws an error asynchronously, using tape?

I am attempting to test this module (receiver.js) for an error thrown:
var request = require('request')
module.exports = function(url){
request({
url: url,
method: 'POST'
}, function(error) {
if(error){
throw error
}
})
}
using this test (test.js):
var test = require('tape')
test('Receiver test', function(t){
var receiver = require('./receiver')
t.throws(function(){
receiver('http://localhost:9999') // dummy url
}, Error, 'Should throw error with invalid URL')
t.end()
})
but tape runs the assertion before the error is thrown, resulting in the following error message:
TAP version 13
# Receiver test
not ok 1 Should throw error with invalid URL
---
operator: throws
expected: |-
[Function: Error]
actual: |-
undefined
at: Test.<anonymous> (/path/to/tape-async-error-test/test.js:5:4)
...
/path/to/receiver.js:9
throw error
^
Error: connect ECONNREFUSED 127.0.0.1:9999
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1062:14)
Is there a way around this?
Generally, using tape, you have to ensure you call assert.end() after the async call has completed. Using promises (would require request-promise and returning the promise):
test('Receiver test', function(t){
// Tells tape to expec a single assertion
t.plan(1);
receiver('http://localhost:9999')
.then(() => {
t.fail('request should not succeed')
})
.catch(err => {
t.ok(err, 'Got expected error');
})
.finally({
t.end();
});
});
Using async/await:
test('Receiver test', async function(t) {
try {
await receiver('http://localhost:9999');
assert.fail('Should not get here');
} catch (err) {
assert.ok(err, 'Got expected error');
}
t.end();
});
The above example is mostly correct but here's a complete working example that compares async to synchronous side by side and also shows how to check for the error message in a manner similar to the tape examples given on tape's README.md.
test('ensure async function can be tested to throw', async function(t) {
// t.throw works synchronously
function normalThrower() {
throw(new Error('an artificial synchronous error'));
};
t.throws(function () { normalThrower() }, /artificial/, 'should be able to test that a normal function throws an artificial error');
// you have to do this for async functions, you can't just insert async into t.throws
async function asyncThrower() {
throw(new Error('an artificial asynchronous error'));
};
try {
await asyncThrower();
t.fail('async thrower did not throw');
} catch (e) {
t.match(e.message,/asynchronous/, 'asynchronous error was thrown');
};
});

Error using Redis Multi with nodejs

I am using Redis and consulting it from nodejs, using the module Redis.
When i exec a client.multi() and the redis server is down the callback doesn't send the error and the nodejs app terminates.
This is the error
/Users/a/db/node_modules/redis/index.js:151
throw callback_err;
^
TypeError: Cannot read property 'length' of undefined
at Command.callback (/Users/a/db/node_modules/redis/index.js:1098:35)
at RedisClient.flush_and_error (/Users/a/db/node_modules/redis/index.js:148:29)
at RedisClient.on_error (/Users/a/db/node_modules/redis/index.js:184:10)
at Socket.<anonymous> (/Users/a/db/node_modules/redis/index.js:95:14)
at Socket.EventEmitter.emit (events.js:95:17)
at net.js:441:14
at process._tickCallback (node.js:415:13)
this is my code:
Constructor class
var redis = require('redis');
var client;
function Redis(){
client = redis.createClient();
client.on("error", function (err) {
console.log("Error " + err);
});
}
Redis.prototype.multi = function(commands,callback){
var err = null;
client.multi(commands).exec(function (error, res) {
if(error){
process.nextTick(function(){
callback(error,null)
})
}else{
process.nextTick(function(){
callback(null,res)
})
}
});
}
FYI, I ran across this in an old lib that depended on old version of node_redis.
This issue was a bug and was fixed in v0.9.1 - November 23, 2013: https://github.com/mranney/node_redis/pull/457
I think that people are still reaching here... (not sure if this answers this specific question directly, but I assume people reaching here since the multi.exec() returns true / the event loop is not waiting for it's response.
After the fixes that went in (in node-redis), it is possible to wrap the result of exec with Promise, and then you will be sure that the result will include the replies from the multi.
So, you can add some redis commands to the multi:
await multi.exists(key);
await multi.sadd(key2,member);
And then in the result do something like:
return new Promise((resolve, reject) => {
multi.exec((err, replies) => {
if (err) {
reject(err);
}
return resolve(replies);
});
});
Otherwise, if you will just do: const reply = await multi.exec();
it will just return you true, and not the replies
** Important to mention - this refers to 'async-redis' and 'node-redis'

Resources