So I want to restore mongo database before tests begin.
I do this way:
const app = require("../app");
const chai = require("chai");
const mongoose = require("mongoose");
const User = require('../models/users');
const Region = require('../models/regions');
const testUsers = require('../testdata/users.json');
const testRegions = require('../testdata/regions.json');
describe('Restoring database', function () {
before(function(done) {
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
console.log('Cleaned database');
done();
});
before(function(done) {
testUsers.users.forEach(element => {
var ObjectId = mongoose.Types.ObjectId;
element._id = new ObjectId(element._id);
var newUser = new User(element);
newUser.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Users added');
done();
});
before(function(done) {
testRegions.regions.forEach(element => {
var newRegion = new Region(element);
newRegion.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Regions added');
done();
});
testdata/users.json and testdata/regions.json are simple json files including key/pair values.
Does this look good?
When I run
npm test
It does not give any error. And in the console I see this:
Restoring database
Cleaned database
Users added
Regions added
But when I look in database I get different results.
Sometimes everything looks good. All the rows are in the collections.
Sometimes a few rows are missing in one of the collections.
Sometimes one of the collections is empty.
This is a very strange behaviour.
I also tried to add in the variable "promises" each of the "newUser" and "newRegion" instead of executing them directly.
But I still get these strange results.
Whats the deal?
The issue is due to done() being called before the async statements/promises have completed.
Either use async/await or use Promises and only call done() when your async statements/promises have completed.
For example:
No done() call as we using await statements which will wait till each statement completes before continuing:
before(async function() {
let userResult = await User.deleteMany();
let regionResult = wait Region.deleteMany();
console.log('Cleaned database');
});
Or use done() with promises:
before(function(done) {
User.deleteMany()
.then(result => {
console.log('Cleaned database');
done();
});
});
The syntax in your before example is not adding Promises at all, it is simply adding those functions to an array:
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
Take a look at the following related answer to help.
Related
I can set the value for a key in my Redis server (when I flushall, run this code, and then get key in redis-cli, I get back the proper values), but when I try to get key values through my NodeJs server, it never even logs out 'got data'.
I considered maybe this was because these functions were running asynchronously, and I was asking to get values that weren't yet stored in the cache, but that does not explain why it wouldn't print 'got data' ever.
My console logs-> 'start'->'data saved'->'end' (and no 'got data', ever)
In redis-cli-> flushall->get test->(nil)->run app.js(in the other terminal)->get test->"1, 2, 3, 4, 5"
I get no errors at all, the code runs, but does not do what I want it to.
Also, don't know if this is relevant, but when connecting to the Redis server, just Redis.createClient() only created a client but did not connect and when I looked it up, the general idea I got was that newer versions did not connect automatically and you had to manually redisClient.connect().
I struggled with this a bit at the start but seemed to have sorted this problem, but just thought I'd mention it, if I messed up somehow, please correct me, as I'm pretty new to NodeJs and codng in general.
My code:
const redisClient = Redis.createClient();
redisClient.connect();
const data = [1, 2, 3, 4, 5];
app.get('/', async(req, res, next) => {
console.log('start')
await redisClient.set('test', data);
console.log('data saved');
await redisClient.get('test', (error, test)=>{
console.log('got data');
console.log(test);
});
console.log('end');
});
Thanks!
I have seen your code. Based on my investigation you should remove the callback and keep await only while you get the data from redis.
I have investigated more this issue and have found that client.get() and client.set() function runs asynchronously. Hence it would achieve this way.
client.set('foo', 'bar', (err, reply) => {
if (err) throw err;
console.log(reply);
client.get('foo', (err, reply) => {
if (err) throw err;
console.log(reply);
});
});
But everytime is not the same use-case that we should set and get the value immediately.
To get rid of this, Following are the options.
Promises and async/await
you can promisify a subset of node_redis functions one at a time using native Node.js promises and util.promisify:
example:
const redis = require('redis');
const { promisify } = require('util');
const runApplication = async () => {
const client = redis.createClient();
const setAsync = promisify(client.set).bind(client);
const getAsync = promisify(client.get).bind(client);
await setAsync('foo', 'bar');
const fooValue = await getAsync('foo');
console.log(fooValue);
};
I have used the await here and solve an issue. In addition to that you can use redis.get().then() also to fetch the data rather than a callback.
I am also attaching the link with an example provided by redis repo
https://github.com/redis/node-redis/blob/master/examples/connect-as-acl-user.js
Following is the code, I have tested and it is working fine now.
redis.js
const redis = require("redis");
const redisClient = redis.createClient({
url: "redis://host:6379",
password: "password",
});
redisClient.connect();
// const { promisify } = require("util");
// promisify(redisClient.get).bind(redisClient);
// promisify(redisClient.set).bind(redisClient);
module.exports = redisClient;
index.js
const express = require("express");
const app = express();
const redisClient = require("./redis");
app.get("/set", async (req, res, next) => {
try {
const data = req.query.p;
await redisClient.set("test", data);
res.status(200).json({
message: "data cached",
data: data,
});
} catch (err) {
console.error(err);
res.status(500).json({
message: "Something went wrong",
});
}
});
app.get("/get", async (req, res, next) => {
try {
// const data = await redisClient.get("test");
const data = await reddisClient.get("test").then((data) => {
return data;
});
res.status(200).json({
message: "Cached data retrieved",
data,
});
} catch (err) {
console.error(err);
res.status(500).json({
message: "Something went wrong",
});
}
});
app.listen(process.env.PORT || 3000, () => {
console.log("Node server started");
});
Please find attached a screenshot of the output.
So the final thought is that, when we are using callback and wants to execute the code synchronously you should either use callback inside callback (but it is created callback hell, so it would not suggested anymore) or you should use promise/async await/native promisify library of nodejs.
Please visit below link to get the simplest understanding and example.
https://docs.redis.com/latest/rs/references/client_references/client_nodejs/
Hope my question clear your mind. I am happy to accept the relevant suggestion to improve an answer.
Here's a simple script i made:
const Nightmare = require('nightmare');
const sql = require('mssql');
const itens = getRecords();
async function getRecords(){
let itensList = [];
const cfg = {
//config here
};
sql.connect(cfg, function(err){
if(err) console.log(err);
let request = new sql.Request();
request.query("<query here>", (err, result) => {
if(err) console.log(err);
itensList = result;
});
return itensList;
});
}
async function getPrices(){
try{
console.log(itens)
}catch(e){
console.log(e);
}
}
getPrices();
Everything works, but when the getPrices() function gets called, here's what's being logged:
Promise { undefined }
What am i missing here?
request.query is being called, but itenslist is being returned before it can be assigned.
Basically, the order of what is happening is:
request.query is called, and starts running the query.
Since request.query is asynchronous, we move on to the next task - returning itenlist
request.query finishes running, and assigns itenlist the expected value after it has already been returned.
To get your desired functionality, I would recommend using callbacks (which node-mssql supports). Alternatively, you could use the await keyword. For instance:
var queryText = 'SELECT 1 AS Value';
var queryResults = await connection.query(queryText);
In node.js I had code like following:
mongoose.connect(dbURI, dbOptions)
.then(() => {
console.log("ok");
},
err => {
console.log('error: '+ err)
}
);
Now i want to do it with async/await syntax. So i could start with var mcResult = await mongoose.connect(dbURI, dbOptions);, afaik it will wait for operation, until it ends with any result (much like calling C function read() or fread() in syncronous mode).
But what should I write then? What does that return to the mcResult variable and how to check for an error or success? Basically I want a similar snippet, but written with proper async/await syntax.
Also I wonder because I have auto reconnect, among dbOptions:
dbOptions: {
autoReconnect: true,
reconnectTries: 999999999,
reconnectInterval: 3000
}
Would it "stuck" on await forever, in case if database connection is unavailble? I hope you can give me a clue on what would happen and how that would work.
Basically I want a similar snippet, but written with proper async/await syntax.
(async () => {
try {
await mongoose.connect(dbURI, dbOptions)
} catch (err) {
console.log('error: ' + err)
}
})()
Please try this, Below code has basics of db connectivity and a query :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let url = 'mongodb://localhost:27017/test';
const usersSchema = new Schema({
any: {}
}, {
strict: false
});
const Users = mongoose.model('users', usersSchema, 'users');
/** We've created schema as in mongoose you need schemas for your collections to do operations on them */
const dbConnect = async () => {
let db = null;
try {
/** In real-time you'll split DB connection(into another file) away from DB calls */
await mongoose.connect(url, { useNewUrlParser: true }); // await on a step makes process to wait until it's done/ err'd out.
db = mongoose.connection;
let dbResp = await Users.find({}).lean(); /** Gets all documents out of users collection.
Using .lean() to convert MongoDB documents to raw Js objects for accessing further. */
db.close(); // Needs to close connection, In general you don't close & re-create often. But needed for test scripts - You might use connection pooling in real-time.
return dbResp;
} catch (err) {
(db) && db.close(); /** Needs to close connection -
Only if mongoose.connect() is success & fails after it, as db connection is established by then. */
console.log('Error at dbConnect ::', err)
throw err;
}
}
dbConnect().then(res => console.log('Printing at callee ::', res)).catch(err => console.log('Err at Call ::', err));
As we're talking about async/await then few things I wanted to mention - await definitely needs it's function to be declared as async - otherwise it would throw an error. And it's recommended to wrap async/await code inside try/catch block.
const connectDb = async () => {
await mongoose.connect(dbUri, dbOptions).then(
() => {
console.info(`Connected to database`)
},
error => {
console.error(`Connection error: ${error.stack}`)
process.exit(1)
}
)
}
connectDb().catch(error => console.error(error))
Lets assume the use of then() is prohibited, you can result to this...
const connectDb = async () => {
try {
await mongoose.connect(dbConfig.url, dbConfigOptions)
console.info(`Connected to database on Worker process: ${process.pid}`)
} catch (error) {
console.error(`Connection error: ${error.stack} on Worker process: ${process.pid}`)
process.exit(1)
}
}
Haven't been able to find any answers to these questions through Twitter or the Mongoose JS Gitter channel and would appreciate some help.
I'm writing an API using a Hapi.JS and Mongoose. I'm using a test database for my integration tests. What I've found though is that if I clear the database after more than one describe block it negatively effects my ability to save and run queries in subsequent describe blocks. I'll leave some annotated code below.
How can I clear the database after each test and not have any race conditions that effect other tests?
'use strict'
//this test will pass when run alone
// it clears the db at the end of it's run
let testConfig = require('../fixtures/fixtures.js')
let User = require('../../models/user.js')
let Bucket = require('../../models/bucket.js')
let BucketFactory = require('../../factories/bucket-factory.js')
let request = require('request')
let bluebird = require('bluebird')
let mongoose = require('mongoose')
mongoose.Promise = bluebird
describe('Buckets API', function() {
it('should get all buckets', function(done){
request.get(`${testConfig.testConfig.testUrl}/buckets`, (err, response, body)=>{
if (err){ throw new Error(err)}
expect(response.statusCode).toBe(200)
done()
})
})
it('should get a bucket by its id', function (done) {
request.get(`${testConfig.testConfig.testUrl}/buckets/${mongoose.Types.ObjectId()}`, (err, response, body)=>{
expect(response.statusCode).toBe(200)
done()
})
});
it('should post it', function (done) {
let testBucket = {name: "Drill", userId: mongoose.Types.ObjectId()}
request.post(`${testConfig.testConfig.testUrl}/buckets`, {json: testBucket}, (err, response, body)=>{
if(err){ throw new Error(err)}
expect(response.statusCode).toEqual(200)
done()
} )
});
// i dont need this afterEach
// but for illustrative purposes it will mess up the latter test
// which will pass if i run it by itself
// but it shouldnt right?
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
})
describe('findByAndUpdate', function () {
beforeEach((done)=>{
// this factory creates and saves a bucket
// i've verified this by checking the test database manually
let newBucket = BucketFactory
done()
})
it('should find and update', function (done) {
Bucket.find({}).exec()
.then((data)=>{
request.put(`${testConfig.testConfig.testUrl}/buckets/${data._id}`, {json: {name: 'Marvel'}}, (err, response, body)=>{
if(err){ throw new Error(err)}
console.log(body)
expect(response.statusCode).toEqual(200)
done()
} )
})
});
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
});
Two questions here:
1) Is Jest a good options to test Node.js (express) APIs?
2) I'm trying to use Jest with Mockgoose, but I can't figure out how to establish the connection and run tests after. Here is my final attempt before coming on SO:
const Mongoose = require('mongoose').Mongoose
const mongoose = new Mongoose()
mongoose.Promise = require('bluebird')
const mockgoose = require('mockgoose')
const connectDB = (cb) => () => {
return mockgoose(mongoose).then(() => {
return mongoose.connect('mongodb://test/testingDB', err => {
if (err) {
console.log('err is', err)
return process.exit()
}
return cb(() => {
console.log('END') // this is logged
mongoose.connection.close()
})
})
})
}
describe('test api', connectDB((end) => {
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3)
})
end()
}))
The error is Your test suite must contain at least one test. This error makes a bit of sense to me but I can't figure out how to solve it. Any suggestions?
Output:
Test suite failed to run
Your test suite must contain at least one test.
Very late answer, but I hope it'll help.
If you pay attention, your describe block has no test function inside it.
The test function is actually inside the callback passed to describe.. kind of, the stack is complicated due to arrow function callbacks.
this example code will generate the same problem..
describe('tests',function(){
function cb() {
setTimeout(function(){
it('this does not work',function(end){
end();
});
},500);
}
cb();
setTimeout(function(){
it('also does not work',function(end){
end();
});
},500);
});
since the connection to mongo is async, when jest first scans the function to find "tests" inside the describe, it fails as there is none.
it may not look like it, but that's exactly what you're doing.
I think in this case your solution was a bit too clever(to the point it doesn't work), and breaking it down to simpler statements could've helped pinpointing this issue
var mongoose = require('mongoose');
// mongoose.connect('mongodb://localhost/animal', { useNewUrlParser: true });
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
var kittySchema = new mongoose.Schema({
name: String
});
var god = mongoose.model('god', kittySchema);
module.exports = god;
code for god.js file
describe("post api", () => {
//life cycle hooks for unit testing
beforeAll(() => {
mongoose.connect(
"mongodb://localhost/animal",
{ useNewUrlParser: true }
);
});
//test the api functions
test("post the data", async () => {
console.log("inside the test data ");
var silence = await new god({ name: "bigboss" }).save();
});
// disconnecting the mongoose connections
afterAll(() => {
// mongoose.connection.db.dropDatabase(function (err) {
// console.log('db dropped');
// // process.exit(0);
// });
mongoose.disconnect(done);
});
});
Jest testing code ..using jest we can we store the name in db