I\m trying to write unit tests for some code I wrote, the problem I'm running into is I expect my mock callback to be called after executing the function but my test fails as it is never called.
describe("Asynchronous specs", function() {
var mockNext;
beforeEach(function() {
mockNext = jasmine.createSpy('mockNext');
var res;
parallelRequests.APICall(testObject[0], null, mockNext);
});
it("callback spy should be called", function () {
expect(mockNext).toHaveBeenCalled();
});
});
The function being tested is quite simple:
function APICall(options, res, next) {
request(options, callback);
function callback(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT') {
return logger.error('request timed out: ', error);
next(error);
}
logger.error('request failed: ', error);
next(error);
}
next(null);
}
}
The issue I suspect is jasmine testing the expectation before the mock callback is executed in API Call due to request's async nature. I've tried using what others suggest of using done() and flags but with no luck. Would appreciate some guidance in this matter.
Your beforeEach code is asynchronous. You have to tell yasmin when your beforeEach logic is done. You can solve this by the callback method done, that is passed to each test. Try this:
describe("Asynchronous specs", function() {
var mockNext;
beforeEach(function(done) {
parallelRequests.APICall(testObject[0], null, function(){
mockNext = jasmine.createSpy('mockNext');
mockNext();
done();
});
});
it("callback spy should be called", function () {
expect(mockNext).toHaveBeenCalled();
});
});
Related
Running a simple redis test
var redis = require(__dirname+'/server/redis.js');
redis.redisTest(function(res){
RedisState = res;
});
redis.js
redisTest: function(callback){
GameDBCon.set('my test key', 'my test value');
GameDBCon.get('my test key', function (error, result) {
if (error) {
callback(false);
console.log(error);
throw error;
}
console.log('GET result ->' + result);
callback(true);
});
},
error from redis.js at callback(true);
callback is not a function, I see a lot of similar issues like this but its not making much sense. Additionally I have similar code that does work.
Shouldn't this callback work?
I'm trying to test my routes file, and mocha is returning success for all of my expects, even though I've coded a couple that should absolutely fail. I added a 2+2 = 5 test just to make sure something would fail. I have done() in my assertion blocks.
I'm using a MEAN stack, and I tried to test the node files with jasmine, since I'm already using that to test the Angular files, but got tons of crazy errors, so I threw all that out and decided to give mocha a try instead.
Results:
Routes
1) makes sure something fails
GET /
√ returns status code 200
GET /nonexistent
√ returns status code 400
GET /api/todos
√ returns status code 200
√ returns a list of todos
Test file
// test/routes.spec.js
var request = require('request');
var expect = require('chai').expect;
describe('Routes', function() {
var base_url = "http://localhost:8080/"
// does fail as expected
it("makes sure something fails", function () {
expect(2 + 2).to.equal(5);
});
describe("GET /", function() {
it("returns status code 200", function() {
request(base_url, function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
});
//should fail
describe("GET /nonexistent", function() {
it("returns status code 400", function () {
request(base_url + "/nonexistent", function (error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
});
describe("GET /api/todos", function() {
it("returns status code 200", function() {
request(base_url + "/api/todos", function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
//should fail
it("returns a list of todos", function() {
request(base_url + "/api/todos", function(error, response, body) {
console.log(body);
expect(body).to.equal("abcd");
done();
});
});
});
});
Routes file:
// app/routes.js
var Todo = require('./models/todo');
module.exports = function(app) {
// api ---------------------------------------------
// get all todos
app.get('/api/todos', function (req, res) {
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
// create todo and send back all todos after creation
app.post('/api/todos', function (req, res) {
Todo.create({
text: req.body.text,
done: false
}, function (err, todo) {
if (err)
res.send(err);
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});
// delete a todo
app.delete('/api/todos/:todo_id', function (req, res) {
Todo.remove({
_id: req.params.todo_id
}, function (err, todo) {
if (err)
res.send(err);
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
})
})
})
// application --------------------------------------
app.get('*', function (req, res) {
res.sendFile(__dirname + '/public/index.html');
});
};
You want to use the done callback but none of your tests declare it in the parameters of the callbacks passed to it. Your first test, for instance, should be:
it("returns status code 200", function (done) { // <== Add parameter here!
request(base_url, function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
Without the parameter, Mocha considers the test to be synchronous. So it does not wait for request to call its callback, and ends right away. The fact that done is undefined does not lead to an error because the JavaScript interpreter does not get to done() before Mocha deems the tests over.
I'm a JavaScript novice and had to change my code from
it('getReports', () => {
getReports()
.then((res) => {
assert.equal(200, res.statusCode);
});
});
to
it('getReports', () => getReports()
.then((res) => {
assert.equal(200, res.statusCode);
}));
i.e. Had to remove the first set of curly brackets.
After this the Mocha tests reported an error.
Starting with Node 8 you can use the native async/await approach for requests and testing.
First use request-promise or request-promise-native instead request.
const request = require('request-promise-native');
Tests with async/await:
// testing success results - any error will fail the test
it('Returns status code 200', async () => {
const response = await request(base_url);
expect(response.statusCode).to.equal(200);
});
// testing for a particular error
it('Testing a particular error is thrown', async () => {
let error;
try {
await request(base_url);
} catch (err) {
error = err;
}
expect(error).to.be.ok;
expect(error.message).to.equal('Expected error message');
});
In my case running test files with the below command solved the problem.
node --unhandled-rejections=strict node_modules/.bin/mocha --require #babel/register --require babel-polyfill test/**/*.test.js
I am trying to use rethinkdb and test it via expresso. I have function
module.exports.setup = function() {
var deferred = Q.defer();
r.connect({host: dbConfig.host, port: dbConfig.port }, function (err, connection) {
if (err) return deferred.reject(err);
else deferred.resolve();
});
return deferred.promise;
});
I am testing it like this
module.exports = {
'setup()': function() {
console.log("in setup rethink");
db.setup().then(function(){
console.log(clc.green("Sucsessfully connected to db!"));
}).catch(function(err){
console.log('error');
assert.isNotNull(err, "error");
});
}
};
And I am runing code like this
expresso db.test.js
But expresso shows error 100% 1 tests even in case of error.
I tried to put throw err; in catch, but nothing changes.
But if I put assert.eql(1, 2, "error"); in the begining of setup() it fails as expected;
Is there something, that caches errors? How can I make it fail as it should be?
For squalize I found
Sequelize.Promise.onPossiblyUnhandledRejection(function(e, promise) {
throw e;
});
Is there something like this for rethink db?
The problem is that this test is asynchronous, and you're treating it as a synchronous test. You need to do the following:
module.exports = {
'setup()': function(beforeExit, assert) {
var success;
db.setup().then(function(){
success = true;
}).catch(function(err){
success = false;
assert.isNotNull(err, "error");
});
beforeExit(function() {
assert.isNotNull(undefined, 'Ensure it has waited for the callback');
});
}
};
Mocha vs Express
You should consider taking a look at mocha.js, which has a much nicer API for asynchronous operations by passing the done function. The same test would look like this:
module.exports = {
'setup()': function(done) {
db.setup().then(function(){
assert.ok(true);
}).catch(function(err){
assert.isNotNull(err, "error");
})
.then(function () {
done();
});
}
};
Promises
The first function you wrote can be re-written in the following manner, because the RethinkDB driver, by default, returns a promise on all operations.
module.exports.setup = function() {
return r.connect({host: dbConfig.host, port: dbConfig.port });
});
I'm trying to mock the function fs.readdir for my tests.
At first I've tried to use sinon because this is a very good framework for this, but is hasn't worked.
stub(fs, 'readdir').yieldsTo('callback', { error: null, files: ['index.md', 'page1.md', 'page2.md'] });
My second attempt was to mock the function with a self-replaced function. But it also doesn't works.
beforeEach(function () {
original = fs.readdir;
fs.readdir = function (path, callback) {
callback(null, ['/content/index.md', '/content/page1.md', '/content/page2.md']);
};
});
afterEach(function () {
fs.readdir = original;
});
Can anybody tell me why both doesn't works? Thanks!
Update - This also doesn't works:
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
Update2:
My last attempt to mock the readdir function is working, when I'm trying to call this function directly in my test. But not when I'm calling the mocked function in another module.
I've found the reason for my problem. I've created the mock in my test class tried to test my rest api with supertest. The problem was that the test was executed in another process as the process in that my webserver runs. I've created the express-app in my test class and the test is now green.
this is test
describe('When user wants to list all existing pages', function () {
var sandbox;
var app = express();
beforeEach(function (done) {
sandbox = sinon.sandbox.create(); // #deprecated — Since 5.0, use sinon.createSandbox instead
app.get('/api/pages', pagesRoute);
done();
});
afterEach(function (done) {
sandbox.restore();
done();
});
it('should return a list of the pages with their titles except the index page', function (done) {
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
request(app).get('/api/pages')
.expect('Content-Type', "application/json")
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var pages = res.body;
should.exists(pages);
pages.length.should.equal(2);
done();
});
});
});
I am using a third party library. Which is using node domain for error handling.
If the callback function passed to that third party library have any error, It end-up in calling my callback multiple times.
Example code is:
var startFunction = function (callback) {
//Call thirdParty function and wait for response
thirdPartyFunction(function (error, data) {
console.log("Called with");
console.log(arguments);
//Assume there is an error in my callback function
setTimeout(function () {
dd
callback.apply(null);
}, 2000);
});
}
//ThirdParty function don't modify anything here
var thirdPartyFunction = function (callback) {
var Domain = require("domain");
var d = require('domain').create();
d.on('error', function (er) {
console.log("hi");
callback.apply(null, er);
});
d.run(function () {
setTimeout(function () {
callback.apply(null, [null, "Hello"]);
}, 1000);
});
};
startFunction(function () {
console.log("Got response")
});
We reported this bug to third party lib and they have modified the source code. Like:
d.on('error', function (er) {
if (isCalled == false) {
isCalled = true;
} else {
return;
}
console.log("hi");
callback.apply(null, er);
});
Now problem of function getting called multiple times is solved. But final callback is never getting called.
How to handle this behavior of node ?
If third party lib modify there code to, It result in application crash. Putting a wrapper domain also not help.
d.on('error', function (er) {
if (isCalled == false) {
isCalled = true;
} else {
throw new Error("Getting called");
return;
}
console.log("hi");
callback.apply(null, er);
});
What is the best method of handing such cases in node ?
You can attach your own domain listener to your callback function like so:
var startFunction = function (callback) {
//Call thirdParty function and wait for response
thirdPartyFunction(function (error, data) {
var d1 = require('domain').create();
d1.on('error', function(er){
console.log("bye");
callback.apply(null, er);
});
d1.run(function () {
console.log("Called with");
console.log(arguments);
//Assume there is an error in my callback function
setTimeout(function () {
dd
callback.apply(null);
}, 2000);
});
})
}
This way if there is an error it will be caught by your handler and the error will be sent back up to the main level and not get caught in the infinite loop.