Mocha js Calling After() too Soon? - javascript

New to Mocha unit testing, I have a few Mocha examples that are running fine, but I have been trying for hours to get this one to run and no matter what I do, after() is called way earlier than I feel it should. Here's an example:
var dummyData = require('./dummyData.js')
describe('mochaTest', function() {
after(function(done) {
dummyData.cleanDb(function(){
done();
})
});
it('should hit the db to get dummy data and send', function(done) {
dummyData.createDummyData(function(data1, data2, Lookup) {
Lookup.lookup({
data1: data1,
data2: data2
}, function(err, result) {
done();
});
});
});
})
And then in dummyData.js:
exports.createDummyData = function(cb){
doSomeStuff(function (err, patient) {
// Connect to db, get some data to pass.
var Lookup = require(./Lookup.js);
cb(data1, data2, Lookup);
})
}
exports.cleanDb = function(cb) {
// Clear db connections.
cb();
}
The problem is that right after the test is run, the after() function gets called and the Lookup function can't hit the db, because the db connection has been cleared. Why is after being called so early, it shouldn't be called until the it statement calls done() right?

This is an old question, but I have experienced the same issue and could not find any explanation to solve this. Unfortunately I do not have enough reputation to answer as a comment, so I'll share how I solved my issue here.
In my controller, I had a method outlined as follows:
exports.insert = (request, response) => {
UserModel.createUser(request.body)
.then(() => {
respond.status(201).send({message: 'User created successfully
});
};
I realized the issue here, was that my test method wasn't waiting for a response from my User.insert(), because this function is void -- it does not return a value. So the following test would jump straight to calling done(); since there was no reason to wait for a response from User.insert(). This was causing my after hook to run prematurely and close the connection to my database before my UserModel could populate.
// Code is shortened, but my request and response was formed using node-mocks-http package
it('should return status 201', function (done) {
User.insert(request, response);
assert(response._getStatusCode() === 201);
done();
};
I hate to say how long this took me to realize this. But, I needed to return a promise from my controller, so that my test function would have something to wait on. By changing my controller to this:
exports.insert = async (request, response) => {
await UserModel.createUser(request.body)
.then(() => {
response.status(201).send({message: "User created successfully"});
})
.catch(() => {
response.status(400).send({message: "There is already an account with that email"});
});
return response;
And with my test looking something like this:
it('should return a status code of 201', async function () {
await User.insert(request, response);
assert(response._getStatusCode() === 201);
});
The database populates successfully, and my test will wait for my promise to fulfill, thus updating the response before calling my assert. Then the after hook works when expected.
I hope this helps someone else who stumbles across this old thread.

Related

Express resolving route before API call completes

Ok, full disclosure I am a hobby coder so I understand there to be gaps in my knowledge. However I've tried all sorts of solutions for this and have been unable to get a working answer.
DESIRED RESULT
I make a call to my Express server, it fetches data from an external API and renders once the data has been retrieved.
PROBLEM
I cannot seem to make Express wait no matter how I lay out the async/await pattern. Currently my code is as follows:
Express.js
app.get('/getInventory', async (req, res) => {
try {
let inventory = await api.getInventory(req.query.id)
console.log(inventory)
res.json(inventory)
}
catch(err) {
console.log(err)
}
})
My api.js is currently as such:
exports.getInventory = async function(id){
let data = await manager.getInventoryContents(id, 570, 2, true, (err, inventory) => {
if (err) {
console.log(err)
Promise.reject(err)
}
else {
console.log('Success')
Promise.resolve(inventory)
}
})
return data
}
In case you're wondering I have the console.log() actions just to try and see when something is happening.
WHAT I AM GETTING SO FAR
With just about every variation I am getting the Express.js inventory as undefined (similarly no data being sent to the client), however I AM receiving a Success message (or even the inventory itself) from api.js.
I am guessing the right syntax is obvious once I complete it but I cannot seem to get it to function properly. What am I missing?
Try this:
exports.getInventory = async function(id){
return new Promise((resolve, reject) => {
manager.getInventoryContents(id, 570, 2, true, (err, inventory) => {
if (err) {
console.log(err);
reject(err);
}
else {
console.log('Success');
resolve(inventory);
}
}
}
}

Firestore took too long to fetch data therefore I couldn't display it

I have a function which fetch a data from Firestore :
getLastTime(collectionName: string) {
const docRef = this.afs.firestore.collection(collectionName).doc(this.User).collection('lastTime').doc('lastTime');
docRef.get().then(doc => {
if (doc.exists) {
this.get = doc.data().lastTime;
} else {
this.get = 'Never done';
}
}).catch(error => {
console.log('Error getting document:', error);
});
return this.get;
}
For my test I actually have a string value inside the doc 'lastTime' which is a string.
Inside ngOnInit(), I called my function and console.log the result
this.InjuredLastTime = this.getLastTime('INJURY');
console.log(this. this.InjuredLastTime);
Normally I should have my string printed inside the console but I got undefined...
Maybe it's because Firestore do not fetch fast enough my data, but I am quiet surprised since Firestore is quiet fast normally...
You don't actually wait for the promise that is created by docRef.get() before you return from getLastTime(). So, unless the call to firebase is instant (e.g. never) this won't work.
The correct solution really depends on what you are doing with this.InjuredLastTime. But one approach would just be to return a promise and set it after it is ready:
getLastTime(collectionName: string) {
const docRef = this.afs.firestore.collection(collectionName).doc(this.User).collection('lastTime').doc('lastTime');
return docRef.get().then(doc => {
if (doc.exists) {
return doc.data().lastTime;
} else {
return 'Never done';
}
}).catch(error => {
console.log('Error getting document:', error);
return null;
});
}
Then, instead of the assignment synchronously, do it asynchronously:
this.getLastTime('INJURY').then(result => { this.InjuredLastTime = result });
Data is loaded from Firestore asynchronously, since it may take some time before the data comes back from the server. To prevent having to block the browser, your code is instead allowed to continue to run, and then your callback is called when the data is available.
You can easily see this with a few well-placed log statements:
const docRef = this.afs.firestore.collection(collectionName).doc(this.User).collection('lastTime').doc('lastTime');
console.log('Before starting to get data');
docRef.get().then(doc => {
console.log('Got data');
});
console.log('After starting to get data');
If you run this code, you'll get:
Before starting to get data
After starting to get data
Got data
This is probably not the order that you expected the logging output in, but it is actually the correct behavior. And it completely explains why you're getting undefined out of your getLastTime function: by the time return this.get; runs, the data hasn't loaded yet.
The simplest solution in modern JavaScript is to mark your function as async and then await its result. That would look something like this:
async function getLastTime(collectionName: string) {
const docRef = this.afs.firestore.collection(collectionName).doc(this.User).collection('lastTime').doc('lastTime');
doc = await docRef.get();
if (doc.exists) {
this.get = doc.data().lastTime;
} else {
this.get = 'Never done';
}
return this.get;
}
And then call it with:
this.InjuredLastTime = await this.getLastTime('INJURY');

node.js : How do I pass in multiple variables into a view?

I'm trying to create a webpage where there is an instance of all the current Projects I am working on, on the left, so I'd need a .forEach() function in order to loop through all of them in order to display it, but on the other side, I need to display the information that is currently selected.
Please first take a look at my code block so I can try to explain the thought process behind what I was trying to do.
So I didn't have any problems selecting the information of the single project that I needed to display on this webpage. I used the .findOne() function in order to pick out the information that I needed.
The problem that I'm facing is that I also need to pass a var that's connected to the .find() function in order to pass through all of the elements of the database. The way I went about this is that I thought I would be able to set the definition of allProjects by manually running the .find() function, and then returning it, thus assigning Projects.find() to allProjects.
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
} else {
res.render('show', {
foundProject: foundProject,
allProjects: Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
return allProjects;
}
})
});
}
});
});
I thought that by returning allProjects and then also having that assigned to allProjects, i'd be able to use the allProjects variable in my show.ejs page.
Unfortunately, I'm getting an error 'allProjects.forEach() is undefined' which leads me to believe that in the app.js where I am defining allProjects, it's not being assigned the correct value that I want it assigned.
It looks like you're expecting return allProjects to do something, but that's actually ignored. Unless you have a callback function you can call, that will go into the void and never be seen by anyone. This is true of virtually all callback functions. They do not care what value that function returns because it's never relevant, what they want is the future value which comes through the callback given to this function.
In other words it plays out like this:
asyncFunctionTakingCallback(function(cb) {
cb(null, value); // This is the important value!
return value; // Nobody cares about this value. Don't even bother.
});
To fix that you need to move the render call inside of the inner-most callback function:
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
// Return here to avoid another level of indentation below
return;
}
Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
res.render('show', {
foundProject: foundProject,
allProjects:
});
}
});
});
});
Now that's still a dizzying amount of code and the nesting here is getting completely out of control even though this is relatively simple Node code.
For comparison here's a version that uses async functions:
app.get('/projects/:url', async (req, res) => {
let foundProject = await Projects.findOne({ Url: req.params.url });
res.render('show', {
foundProject: foundProject,
allProjects: await Projects.find({})
});
});
There's really not much to it this way. What await does is basically stall out on that line and wait for the promise to get resolved or produce an error. Any errors produced should be captured with try { ... } catch as usual.

save() doesn't work on mongoose.Schema

const Data = require('./Models/Data');
...
let example = new Data( sample );
example.save( function ( err ){
console.log('test);
if ( err ) {
console.log('Error saving Data. 'Error: ', err);
}
});
Any ideas why save() callback function never runs? I mean, the "test" text doesn't show up, while "example" is created just like it should (I mean, when I print it, it looks ok).
Any ideas? TIA
Mongoose async save() function works with a function, which means you don't need to pass it a callback function, but rather use then/catch pattern:
const Data = require('./Models/Data');
...
let example = new Data( sample );
example.save()
.then(() => {
console.log('test);
})
.catch((err) => {
console.log('Error saving Data. 'Error: ', err);
});
see more here
As Nir Levy has already said: the then call can be an alternate way of saving the document.
You can also try supplying the second argument in the save's callback as:
example.save((err, doc) => {
console.log('test');
if (err) {
console.log('Error while saving data: ', err);
} else {
console.log('document is: ', doc);
}
});
Also note that you missed a closing quotemark on the console.log() inside example.save callback
Can you also make sure that your mongodb server is running and you are connected to the mongodb server? if that maybe causing the problem?
This typically occurs when you haven't connected to the database. You can create the model object but none of the Mongoose functions that operate on the database works. They fail silently instead. Make sure you connect with the connect function. You can also listen to the connection and error events to see that you actually get connected:
const dburl = `mongodb://localhost/testdb`;
mongoose.connect(dburl, { useMongoClient: true });
mongoose.connection.on('connected', () => { console.log(`Mongoose connected to ${dburl}`); });
mongoose.connection.on('error', (err) => { console.log(`Mongoose connection error: ${err}`); });
mongoose.connection.on('disconnected', () => { console.log('Mongoose disconnected'); });
Pay attention to the terminal when you start the app and look for Mongoose connected. Finally, you can turn on debug mode in Mongoose to see what's actually going on behind the scenes:
mongoose.set('debug', true);
You can add it right below the connect call, for example.
You don't have to use the promise method by the way. You can use the callback version that you're already using. Just make sure to correct the error with the missing ' in the console.log.

How to test a function which consumes a callback? Or how to defer the assertion?

I use Jest to test a function which generates a JSON Web Token. It seems that I can't assert the value since when I assert, the callback hasn't been executed yet.
const issueJWT = function issueJWT(req, res, next) {
jwt.sign(signUser, function (err, token) {
if (err) {
next(err);
return;
}
res.locals.token = token;
next();
});
};
This is my test, I mock the request and response, then assert the result:
test('Should return a JWT with proper value if nothing wrong happened', () => {
issueJWT(request, response, mockNext);
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
});
The error is:
TypeError: Cannot read property 'payload' of null
How to make it work?
According to my knowledge, I think the callback is at the task queue which
means it will be executed when nothing is in the event loop, right? I wanna find a way to defer my assertion, but don't know how...
Thanks for the tips, I use the done, now the test could pass, but the problem is, whenever there is a problem, the error message doesn't make any sense... Any problem to my solution?
test('Should return a JWT with proper value if nothing wrong happened', (done) => {
const callback = () => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
expect(tokenPayload).toHaveProperty('iss');
done();
};
issueJWT(request, response, callback);
});
The error is now:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Ok, so with some help from #felixKling getting me to actually read the docs, you need to do something like this:
test('Should return a JWT with proper value if nothing wrong happened', done => {
issueJWT(request, response, (e) => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
done();
});
});
I'm not on my dev box so I can't test this, but basically the idea is that you use the 'done' parameter to the test callback to signal that the test is waiting on async code. The test framework will basically wait for your test to call that callback before exiting.
In this case, your next() call from issueJWT is what we're waiting on firing before checking to see if the various objects were updated. If you were not using next() in your middleware, you'd likely need to mock whatever response method you're calling instead (e.g. response.end()) to do your tests.

Categories