Test using mongodb-memory-server is failing/timing out - node.js

I’m setting up mongodb-memory-server in my backend for test purposes and am experiencing some issues when running tests that I need to debug. My issue is that when I run my test (which will create a mongodb doc somewhere in the service being tested), the test times out.
As I understand it, this is because when the test is executed and a new mongo doc is trying to be created during the test, I console log mongoose.connection.readyState and it says it’s 0, meaning that mongoose is disconnected. This is strange to me because I added console logs to my connectMongoose() function (pictured below) and it says that mongoose is connected.
So my main question is why does it say mongoose is connected at the end of connectMongoose(), but it says it’s disconnected during the execution of the unit test/service function? How can I ensure that MongoDB-memory-server is fully connected prior to test execution?
Below is a screenshot showing how I am doing the mongoose test connection:
Below this is a screenshot of exactly where and how mongodb-memory-server is being used:
Here is a screenshot of my jest.config.js:
And finally the actual test file which has the failing test (what I’m asking about):

beforeAll(connectMongoose)
beforeEach(clearDatabase)
afterAll(disconnectMongoose)
Your three functions here are async functions, but you don't await them - is it possible that the connect Mongoose returns whilst the promise is still awaiting, and the other code continues despite the async function not having completed yet?
Perhaps this would better serve your purpose?
beforeAll(() => {
await connectMongoose
})

Before :
beforeAll(connectMongoose)
beforeEach(clearDatabase)
afterAll(disconnectMongoose)
After:
beforeAll(async() => {await connectMongoose})
beforeEach(async() => {await clearDatabase})
afterAll(async () => { await disconnectMongoose})
The reason is you should wait until the mongoose connection is done completely and remove
set timeout in connectMongoose function not needed there.If you want to use jest timeout you can use it in the beforeEach function.

Related

How to run multiple integration tests using mocha recursively without getting their hooks all combined?

I have made a few integration test using mocha which run fine when run independently but when i try to run them using : mocha test --recursively .
The behaviour I noticed here is that all the after hooks (probably the before too) are getting combined.
I drop my db in the after hook of each test so I check in between tests and I can find data from the previous tests.
It gets cleared up after the last test somehow.
I have already tried importing them into one file but even that won't serve the purpose.
Here are my hooks.
before(async () => {
app.set('port', SERVER_PORT);
server = http.createServer(app);
server.listen(SERVER_PORT, () => console.log(`API running on localhost:${SERVER_PORT}`));
// Initial feeding of the database
await dookie.push('mongodb://localhost:27017/tests', SEEDDATA);
});
after(async () => {
await mongoose.connection.db.dropDatabase();
server.close();
process.exit(0);
});
THANKS
Use jest as it provides the functionality you're looking for inbuilt.
It's hard to tell what is wrong with your tests without having a closer look at the code, so I'm going to drop here a few ideas that come to my mind, with no guarantee that anything will help.
Possibility 1
Use beforeEach and afterEach rather than before and after. This will ensure that your DB cleanup code is executed after each test, rather than after the last test in a describe function block. Details here.
Possibility 2
You are running your tests in multiple threads with mocha-parallel-tests or some other tool. Make sure that the tests where the DB is being accessed are not being parallelized.
Possibility 3
Your db.dropDatabase call returns before the database is actually dropped, while the request is still pending. You'll have to check your connection or database settings.
If nothing helps, try inserting log statements at the start of each unit test and before/after hook, this will help you understand when the code is actually being run and see what is happening in the wrong order.

Reusing same Puppeteer instance in all Jest tests

Problem
I'm replacing CasperJS with Jest + Puppeteer. Putting everything in one file works great:
beforeAll(async () => {
// get `page` and `browser` instances from puppeteer
});
describe('Test A', () => {
// testing
});
describe('Test B', () => {
// testing
});
afterAll(async () => {
// close the browser
});
Now, I don't really want to keep everything in one file. It's harder to maintain and harder to run just part of the tests (say, just 'Test A').
What I've tried
I've looked at Jest docs and read about setupScript. It would be perfect, but it runs before every test file. I don't want this because puppeteer setup takes quite a lot of time. I want to reuse same browser instance and pay the setup cost only once no matter how many test files I'll run.
So, I thought about:
// setup puppeteer
await require('testA')(page, browser, config);
await require('testB')(page, browser, config);
// cleanup
This solves modularization, reuses same browser instance, but doesn't allow me to run tests separately.
Finally, I stumbled upon possibility to create a custom testEnviroment. This sounds great but it isn't well documented, so I'm not even sure if env instance is created per test file, or per Jest run. Stable API is also a missing a setup method where I could set up puppeteer (I'd have to do that in constructor that can't be async).
Why I'm asking
Since I'm new to Jest I might be missing something obvious. Before I dig deeper into this I though I'll ask here.
UPDATE (Feb 2018): Jest now have official Puppeteer guide, featuring reusing one browser instance across all tests :)
It was already answered on Twitter, but let's post it here for clarity.
Since Jest v22 you can create a custom test environment which is async and has setup()/teardown() hooks:
import NodeEnvironment from 'jest-environment-node';
class CustomEnvironment extends NodeEnvironment {
async setup() {
await super.setup();
await setupPuppeteer();
}
async teardown() {
await teardownPuppeteer();
await super.teardown();
}
}
And use it in your Jest configuration:
{
"testEnvironment": "path/to/CustomEnvironment.js"
}
It's worth to note, that Jest parallelizes tests in sandboxes (separate vm contexts) and needs to spawn new test environment for every worker (so usually the number of CPU cores of your machine).

MongoDB randomly fail to drop sharded database

I have few integration tests that use real MongoDB database version 3.2. Tests are run with Mocha and I'm dropping the test db after each test with following code. It uses Mongoose version 4.4.12 and native MongoDB Node.js driver.
afterEach((done) => {
const connection = mongoose.createConnection(config.db.host);
connection.once("open", () => {
connection.db.command({ dropDatabase: 1 })
.then((res) => done(null, res))
.catch(done);
});
})
It worked nicely until I enabled sharding in my local environment to cover that with tests as well. Dropping database started to fail randomly. Above code fails to drop my test db about on every third test run. My sharding setup is minimal and have one config server, one mongos and two mongo shards.
After I added some logging I noticed that connection.db.command({ dropDatabase: 1 }) returns { info: 'database does not exist', ok: 1 } on those failed runs. Database is not dropped because MongoDB thinks it is already dropped even it is not the case.
Am I doing it completely wrong? Is there some Mongoose / MondoDB Node.js native driver configuration or such that I'm missing here? Or is this some known problem? Any help is much appreciated.
EDIT: Same thing happens if I use MongoClient directly, so Mongoose is not causing this.

nodejs and TAPE integration issues. App initialization call gets blocked

I am creating unit tests for nodejs app using "tape". When I run tests, the process gets blocked forever. When I dug deeper, I found that if I run "nodejs mainapp.js" the process doesn't end. That's also blocking running of unit tests.
Looks like nodejs process will not end if there are events registered. In my case, creating a mongo or redis connection or use of node modules such as "node-cache" results into this. It's hard to imagine that all such modules can be avoided.
Any suggestion on how to get around the problem. My test case is really simple so there are on issues there.
By the way, I have verified nodejs doesn't get blocked due to http.listen. Someone could argue about using mockups, I am not looking to do that.
test('dummy test', function(t) {
t.equal('hello world', 'hello world', 'Yes hello world = hellow world');
t.end();
});
The main cause of tape processes keeping alive are MongoDB connections not closed. The easiest way to close a connection when all tests ends, is to create a blank test with instructions to close the MongoDB connection.
The following is an example, in this case using Mongoose:
var test = require('tape');
var mongoose = require('mongoose');
test('test', function (t) {
t.pass('Example');
t.end();
});
test('finish', function (t) {
mongoose.disconnect();
t.pass('closing db connection');
t.end();
});
if the problem is indeed a MongoDB connection, a more proper way to deal with it would be using test.onFinish(fn) according to documentation:
The onFinish hook will get invoked when ALL tape tests have finished
right before tape is about to print the test summary.

Adding a default before() function for all the test cases (Mocha)

I'm writing functions for my node.js server using TDD(Mocha). For connecting to the database I'm doing
before(function(done){
db.connect(function(){
done();
});
});
and I'm running the test cases using make test and have configured my makefile to run all the js files in that particular folder using mocha *.js
But for each js file I'll have to make a separate connection to the database, otherwise my test cases fail since they do not share common scope with other test files.
So the question is, Is there anything like beforeAll() that would just simply connect once to the database and then run all the test cases? Any help/suggestion appreciated.
You can setup your db connection as a module that each of the Mocha test modules imports.
var db = require('./db');
A good database interface will queue commands you send to it before it has finished connecting. You can use that to your advantage here.
In your before call, simply do something that amounts to a no op. In SQL that would be something simple like a raw query of SELECT 1. You don't care about the result. The return of the query just signifies that the database is ready.
Since each Mocha module uses the same database module, it'll only connect once.
Use this in each of your test modules:
before(function(done) {
db.no_op(done);
});
Then define db.no_op to be a function that performs the no op and takes a callback function.

Resources