Clearing collection before testing with Mocha - node.js

I am writing unit tests for my Node.js app using Mocha, Chai, and Mongoose. The tests themselves work if the collection is empty(as desired), but I'm having trouble clearing the collection before testing.
let mongoose = require("mongoose");
let Subject = require('../Subject/subject');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
// let server = require('../server');
let server = "http://localhost:3000"
let should = chai.should();
chai.use(chaiHttp);
describe('subjects', () => {
before((done) => { //Before each test, empty the database
Subject.remove({})
done();
});
describe('/GET subject', () => {
// working test
});
describe('/POST subject', () => {
// working test
});
describe('/GET subject', () => {
// working test
});
});
I have also tried variations of
Subject.deleteMany({}, (err) => console.log(err));
and
Subject.find({}, (subject)=>{
subject.remove({}).exec()
})
inside the before block to no avail. I have tried the removes outside of the before block as well, just in case. If I console.log(Subject.remove({})) I get the Subject object, so it's able to access it, just not actually doing anything permanent with it.
I've been at this a couple hours now and haven't gotten any closer to figuring it out, so all help is appreciated.

Try waiting for the callback of the remove call like so
before((done) => { //Before each test, empty the database
Subject.remove({}, done);
});

Since both Mocha and Mognoose support promises for asynchronous blocks, they can be seamlessly used instead of calling done directly. In this case a promise should be returned:
before(() => Subject.remove({}));

Related

Best way to setup files for jest and mongodb

Hihi.
I'm having a problem where jest it's sometimes failing a couple of tests randomly, most of the time because of this error "mongodb memory server cannot perform operation: a background operation is currently running for collection".
In another post I read something about building differents mongo instance for each block of tests.
What I have so far is a globalsetup file where I start the mongo replica set like this:
// global.ts
import { MongoMemoryReplSet } from "mongodb-memory-server";
const replSet = new MongoMemoryReplSet({
replSet: { storageEngine: "wiredTiger" },
});
module.exports = async () => {
await replSet.waitUntilRunning();
const uri = await replSet.getUri();
process.env.MONGODB_URI = uri;
};
and my db.ts is like this
// db.ts
export const connect = async () => {
mongoose.set("useFindAndModify", false);
const conn = mongoose.connect(
process.env.MONGODB_URI || config.connectionString, connectionSettings
);
When trying to call it from a test file I do something like this
// test.spec.ts
import db from "./db";
beforeAll(async () => {
await db.connect();
});
afterAll(async (done) => {
await db.dropCollections();
await db.disconnect(done);
});
beforeEach(async () => {
await seed();
});
describe('Some test', () => {
it('Should not fail and get the seeders', () => {
// some random tests using the seeds values
})
})
What I read in that post is instead of using globalSetup use a setupFile that will run for every test instead of one globally and then I MIGHT be able to solve the concurrency issue I have with my tests.
So, to conclude, does anyone knows if there is a proper way to configure the mongodb in memory or if I am doing something THAT BAD that is allowing this to happend or if is there any kind of improvement I can do that will prevent "mongodb memory server cannot perform operation: a background operation is currently running for collection" this to happen?

chai-as-promised's eventually passes wrong rejection

suddenly I realized that in many of my tests it wrongly passes the fail test, I tried to understand the reason, here's is an example that passes the test which is wrong
describe('...', () => {
it('...', () => {
return chai.expect(Promise.reject('boom')).to.eventually.not.rejected;
});
});
can anyone help me to figure out what am doing wrong? thanks
to.not.eventually is not the same as to.eventually.not
You’re using not, it should be before the eventually
So change your test to below code to use to.not.eventually to see it won’t get passed and it’ll fail
const { describe, it } = require('mocha');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const { expect } = chai;
describe('...', () => {
it('...', () => {
return expect(Promise.reject(new Error('boom'))).to.not.eventually.rejected;
});
});

Mocha times out calling async promise chain in Before hook despite using done

I'm running suite of async integration tests into a mongoose db using mocha and chai on node.js. Most are running fine but for one I have to carry out some pre-test db-prep with the before hook. When I use Done in the before hook Mocha times out.
Error I'm getting is "Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/donfelipe/Sites/Agents/test/agents.js)"
Have tried moving the done() into a finally block at the end of the promise chain, but that just results in the the it block running before the async chain has finished executing. Kind of stumped.
/*
test case to check that the get function returns
*/
process.env.NODE_ENV = 'test-db';
'use strict'
const mongoose = require("mongoose");
const schemas = require('../app_api/models/schemas');
const agents = require('../app_api/controllers/agents.js');
const lists = require('../app_api/controllers/lists.js');
const server = require('../app.js');
const assert = require('assert')
const config = require('config')
//Require the dev-dependencies
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
chai.use(chaiHttp);
describe('Agents test Suite', () => {
beforeEach(() => {
//don't have anything too do in this case
})
describe('/GET listAddItem suite', function () {
//before we can test deletion we have to add at least one list in.
const userModel = mongoose.model('User', schemas.userSchema);
const agentModel = mongoose.model('Agent', schemas.agentSchema);
let agentToAddId = {}
const listObject = {
listName: 'testList',
_id: mongoose.Types.ObjectId(),
agents: [{
_id: "00000000000000000000000",
name: "Test agent"
}]
}
const lengthToTestAgainst = listObject.agents.length
beforeEach((done) => {
userModel.findById(config.defaultUserId)
.select('agentList')
.then((parentDoc) => {
if (!parentDoc) {
console.error('Unable to find user');
}
parentDoc.agentList.push(listObject)
return parentDoc.save()
})
.then(() => {
return agentModel.find({})
})
.then((response) => {
agentToAddId = response[0]._id
//console.log(response[0]._id);
done()
})
})
it('should add a new item into the testList', (done) => {
chai.request(server)
.get(`/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}`)
.end((err, response) => {
response.should.have.status(200)
response.body.agentList.testList.should.not.be.equal(lengthToTestAgainst + 1)
done(err)
})
})
})
})
Duh. Resolved this myself. A real case of the tail wagging the dog.
So I mocked up the call to the API that the chai.request(server) is making:
/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}
and submitted it through POSTMAN. Turns out there was a bug in the API and so it was not returning a response, so the timeout I was getting from Mocha was a valid response, the request was just hanging.

Mocha tests with async initialization code

I am writing tests for a REST client library which has to "login" against the service using the OAuth exchange. In order to prevent logging in for every endpoint I am going to test I'd like to write some sort of "test setup" but I am not sure how I am supposed to do this.
My test project structure:
test
endpoint-category1.spec.ts
endpoint-category2.spec.ts
If I had only one "endpoint category" I had something like this:
describe('Endpoint category 1', () => {
let api: Client = null;
before(() => {
api = new Client(credentials);
});
it('should successfully login using the test credentials', async () => {
await api.login();
});
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
My Question:
Since the login() method is the first test there, it would work and the client instance is available for all the following tests as well. However, how can I do some sort of setup where I make the "logged in api instance" available to my other test files?
Common code should be moved to beforeEach:
beforeEach(async () => {
await api.login();
});
At this point should successfully login using the test credentials doesn't make much sense because it doesn't assert anything.
describe('Endpoint category 1', () => {
let api: Client = null;
beforeEach(() => {
api = new Client(credentials);
});
afterEach(() => {
// You should make every single test to be ran in a clean environment.
// So do some jobs here, to clean all data created by previous tests.
});
it('should successfully login using the test credentials', async () => {
const ret = await api.login();
// Do some assert for `ret`.
});
context('the other tests', () => {
beforeEach(() => api.login());
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
});
Have you had a look at https://mochajs.org/#asynchronous-code ?
You can put in a done-parameter in your test functions and you will get a callback with this you have to call.
done() or done(error/exception)
This done would be also available in before and after.
When calling done() mocha knows your async-code has finished.
Ah. And if you want to test for login, you shouldn't provide this connection to other tests, because there is no guarantee of test order in default configuration.
Just test for login and logout afterwards.
If you need more tests with "login-session", describe a new one with befores.

Test Node.js API with Jest (and mockgoose)

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

Resources