I have written own thin mongodb wrapper for Node.js to eliminate code repetition.
However, I am having problems with asynchronous unit tests run with Mocha and Should.
What happens is that any thrown exception by Should library is being caught by MongoDB driver instead of Mocha.
I.e., neither Mocha catches the error, nor done() function gets called. As a consequence, Mocha prints out an error Error: timeout of 2000ms exceeded.
Snippet of wrapper module db.js
var mongodb = require('mongodb').MongoClient;
exports.getCollection = function(name, callback) {
mongodb.connect(dbConfig.dbURI, {auto_reconnect: true}, function(err, db) {
if (err)
return callback(err, null);
db.collection(name, {strict: true}, callback);
});
};
Mocha test.js
var should = require('should');
var db = require('./db.js');
describe('Collections', function() {
it.only('should retrieve user collection', function(done) {
db.getCollection('user', function(err, coll) {
should.not.exist(err);
coll.should.be.a('object');
// HERE goes an assertion ERROR
coll.collectionName.should.equal('user123');
done();
});
});
});
The same behaviour can be simulated by this simple test.js
var should = require('should');
var obj = {
call: function(callback) {
try {
console.log('Running callback(null);');
return callback(null);
}
catch(e) {
console.log('Catched an error:', e);
}
}
};
describe('Test', function() {
it('should catch an error', function(done) {
obj.call(function(err) {
should.exist(err);
done();
});
});
});
Is there any way to workaround the issue? There must be a way to test such code.
Just by an accidental luck I spotted a GitHub fork dealing with a different problem, but the code lead me to realise I can use a simple trick to make Mocha to catch the assert exceptions:
describe('Test', function() {
it('should catch an error', function(done) {
obj.call(function(err) {
try {
should.exist(err);
done();
} catch (err) {
done(err);
}
});
});
});
I.e. wrapping the should calls into try/catch block and calling done(err) in the catch section does exactly what is expected :
Test passes successfully if no assertion error occurs
Test fails in case of assertion error thanks to done() function accepting an error argument
Related
var promise = require('promise');
var {Builder, By, Key, until} = require('selenium-webdriver');
var test = require('selenium-webdriver/testing');
var chai = require('chai');
var getUrl = require('./wdio.conf.js');
var driver = new Builder().forBrowser('chrome').build();
test.describe('Proper Testing', function() {
test.it('should prompt the server from user', function() {
return new promise(function(resolve,reject){
resolve(driver.get("https://www.google.co.in"));
reject(err);
})
})
})
For code given above, running mocha proper.js opens the given url in chrome browser but the test fails with timeout error.
I've read that done() need not be called if test is returning a promise.
What's wrong in the given code?
Yo have to call done when your are done testing.
I'm not sure what you are trying to achieve here, but this example code should make my point:
describe('Proper Testing', function() {
it('should prompt the server from user', function(done) {
return getUrl().then(function(url){
driver.get(url)
.then(function () {
done();
}).catch(function (err) {
done(err);
});
})
});
})
Reference: https://mochajs.org/#asynchronous-code
Regarding to your problem, try to add a before statement setting a high timeout. Ordinary tests has a default timeout (I think it is 2 seconds), and if the test exceed this time, it raise an error and it doesn't pass the test.
before(function (done) {
this.timeout(5000);
done();
})
Regarding to done: If you are returning a promise, you don't need to use done. Simply declare function() without any parameters, and if it doesn't raise any exception and it accomplish all asserts it finds it pass the test.
describe('Proper Testing', function() {
it('should prompt the server from user', function() {
return getUrl().then(function(url){
driver.get(url);
})
});
})
I write some tests using jasmine and protractor i want in the #beforeeach to execute .exe file using require('child_process') and then #aftereach i will restart the browser.
The problem is that the .exe file is executed only once with the first spec.
here is the code in the beforeEach()
beforeEach((done) => {
console.log("before each is called");
var exec = require('child_process').execFile;
browser.get('URL');
console.log("fun() start");
var child = exec('Test.exe', function(err, data) {
if (err) {
console.log(err);
}
console.log('executed');
done();
process.on('exit', function() {
child.kill();
console.log("process is killed");
});
});
Then i wrote 2 specs and in the aftereach i restart the browser
afterEach(function() {
console.log("close the browser");
browser.restart();
});
You should use the done and done.fail methods to exit the async beforeEach. You begin to execute Test.exe and immediately call done. This could have undesired results since the process could still be executing. I do not believe process.on('exit' every gets called. Below might get you started on the right track using event emitters from the child process.
beforeEach((done) => {
const execFile = require('child_process').execFile;
browser.get('URL');
// child is of type ChildProcess
const child = execFile('Test.exe', (error, stdout, stderr) => {
if (error) {
done.fail(stderr);
}
console.log(stdout);
});
// ChildProcess has event emitters and should be used to check if Test.exe
// is done, has an error, etc.
// See: https://nodejs.org/api/child_process.html#child_process_class_childprocess
child.on('exit', () => {
done();
});
child.on('error', (err) => {
done.fail(stderr);
});
});
How to get asserts in mocha before blocks to work? If I am not suppose to do this let me know. Currently, using promises, if I get an error using an catch block, I'll add an assert to fail the before block. WHat I want is it to fail the describe block, but instead I get two possible outcomes. 1. My test suite completely crashes, 2. I have to wait for each timeout to hit for each test because the before block failed.
before(function (done) {
promise()
.then(function () {
done();
})
.catch(function (error) {
assert(!error);
done();
});
});
I even tried this, thinking, maybe the done was never called.
before(function (done) {
promise()
.then(function () {
//no done here
})
.catch(function (error) {
assert(!error);
});
.finally(function () {
done();
});
});
So far to avoid crashing and waiting, and to make it work, I have done this:
var beforeError;
before(function (done) {
promise()
.then(function () {
done();
})
.catch(function (error) {
beforeError = error;
done();
});
});
it('test something', function () {
assert(beforeError, 'Before block failed with error.');
});
I am really curious if there is a better way to go about this so that if my before/beforeEach/after/afterEach blocks fail, it doesn't cause me to wait ages or my suite to crash! Thanks S/O community! :)
I can't speak to your use of the done callback, but mocha 3.0 supports promises in before hooks now. Were I to write this, I would let the returned promise throw its own error, which will fail the before hook without breaking the suite.
before(function () {
return promise(<async behavior here>);
});
I run many Mocha tests that look like this :
it('should fail blabla', function () {
return <functionThatReturnsAPromise>()
.then(function () {
assert(false);
}, function (err) {
assert.strictEqual(err.message, 'Foobar is undefined');
assert.strictEqual(err.code, 'EUNDEFINEDFOOBAR');
});
});
The only things that vary among my tests are the function, the code and the message.
I wanted to factorize the code like this:
function shouldBeRejected(promise, code, message) {
return promise
.then(function () {
assert(false);
}, function (err) {
assert.strictEqual(err.code, code);
assert.strictEqual(err.message, message);
});
}
(...)
it('should fail blabla', function () {
return shouldBeRejected(<functionThatReturnsAPromise>(),
'EUNDEFINEDFOOBAR', 'Foobar is undefined');
});
It works but when some tests failed, all my backtraces reference the shouldBeRejected function and so, I'm losing all information about the real test that failed.
Is there a way to get a more "intelligent" backtrace pointing to the shouldBeRejected calls that indirectly raise the error? Maybe with a captureStackTrace somewhere?
The question:
I'm using Chai to do the tests and I seem to be stuck on testing an expected error:
Chai expected [Function] to throw an (error)
Current code:
Here's the code of the test:
describe('Do something', function () {
it('should remove a record from the table', function (done) {
storage.delete(ID, done);
});
it('should throw an error when the lookup fails', function () {
expect(storage.delete.bind(storage, ID)).to.throw('Record not found');
});
});
Here's the code of the function:
delete: function (id, callback) {
// Generate a Visitor object
visitor = new Visitor(id);
/* Delete the visitor that matches the queue an
cookie provided. */
tableService.deleteEntity(function (error, response) {
// If successful, go on.
if (!error) {
// Do something on success.
}
// If unsuccessful, log error.
else {
if (error.code === 'ResourceNotFound') {
throw new Error('Record not found');
}
// For unexpected errros.
else {
throw new Error('Table service error (delete): ' + error);
}
}
if (callback) callback();
});
},
Attempted solutions:
I've tried multiple variations of calling expect function (including calling anonymous function:
expect(function() {storage.delete(ID);}).to.throw('Record not found');
Bind, as provided in the example,
and the basic one of
expect(storage.delete(ID)).to.throw('Record not found');
I've also tried substituting the throw parameter from 'Record not found' to multiple things including directing the input to an already created error (Error), and creating a new error in the parameter (new Error('Record not found'));
Possible causes:
I have a suspicion that the error is not being thrown because it takes a while for the test to communicate with the database to delete the record, however I am not sure of how I could remedy that.
Additionally, it seems that the test that runs right after this one actually returns the error that was supposed to be returned on THIS test.
Given (from comments) that tableService.deleteEntity is asynchronous, it is impossible to test that throw. And the code itself is invalid. Because the thrown exception won't be caught, it will be unhandled as it was thrown in a different tick. Read more about Asynchronous error handling in JavaScript and unhandled exceptions in Node.js
In other words such a function cannot be tested for throwing errors:
function behaveBad(){
setTimeout(function(){
throw new Error('Bad. Don\'t do this');
}, 50);
}