Jest run code before and after all of the tests - node.js

I would like to run code before and after all the tests, not file wide, test wide. For example;
Before starting the e2e tests, I would like to run the server in test mode with the database. After all, I would like to flush my db and close the processes.
I don't know if it is possible but I also would like to have a global db variable to do tests. What I am currently doing is like this:
describe("Posts Module", () => {
let dbService: DatabaseService;
beforeAll(async () => {
dbService = new DatabaseService();
await dbService.init();
});
it("should give the posts", () => {
supertest(app)
.get("/posts")
.expect(200)
.then(async (response) => {
const dbPosts = await dbService.getPosts();
expect(response.body).toBeDefined();
expect(response.body.posts).toEqual(dbPosts);
});
});
afterAll(async () => {
await flushDb(dbService);
await dbService.close();
});
});
But what I actually want is initing this database service only once before all of the module tests (also starting the server, currently I start the server manually and run the tests afterwards).

Related

How to prevent Jest from running tests as globalSetup is still running?

My global setup code creates a dynamodb table and the global teardown destroys it. The code is just generic dynamodb.createTable() and dynamodb.deleteTable()
My basic test that just wants to return a list of inserted entities:
describe('Test suite', () => {
beforeAll(async () => {
await InsertEntity()
})
test('test', async () => {
const response = await getEntities()
expect(response.success).toBe(true)
})
})
My InsertEntities code:
export const InsertEntities = async () => {
const entity = new Entity({
// some data
})
const result = await entity.save()
console.log('entity', result)
}
But as soon as I run jest, it starts running the tests before the table is up, so it fails horribly. What can I do to stop this behavior?
What have I already tried: Running jest with --runInBand, adding jest.setTimeout() with various different timings going as up as 20secs. Changing the beforeAll to beforeEach. Checking how much time the table needs to go up: 10secs.
I've changed this code to run as setupFilesAfterEnv, but it still crashed. I've written similar code before and it still works! But not this time.
So, what am I missing from jest flow?

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?

Possible collisions of asynchronous unit tests against a test database in Mocha

I am new to the javascript ecosystem, and I am trying to wrap my head around a decent way to do some basic unit testing against the REST API using a stored set of fixtures, i.e. .json files.
My entry point sets up a testing environment by connecting to a test database and emits a ready state.
if (!process.env.TESTING) {
/* Connect to MongoDB */
connect(process.env.DEV_DB_HOST, process.env.DEV_DB, () => app.emit(ready));
app.on(ready, () => {
app.listen(port, () => console.log(`Listening on port ${port}!`));
});
} else {
connect(process.env.TEST_DB_HOST, process.env.TEST_DB, () => app.emit(ready));
}
export default app;
Once the connection is established, the collection recreated in the test database using fixtures and the tests are executed.
import Paper from '../models/paper.js';
describe('Home Page', () => {
before(async() => {
await new Promise((resolve, reject) => {
app.on(ready, () => resolve());
}).then(async() => {
try {
await Paper.collection.drop();
} catch(error) {
/* Mongo throws an exception if the collection in question does not exist */
}
await Paper.create(fixtures);
});
});
after(async() => {
await Paper.collection.drop();
});
it('Returns a list of items ordered by date in descending order', async() => {
const response = await chai.request(app).get('/');
expect(response.body).to.have.lengthOf(2);
expect(response.body).to.be.sortedBy("date", { descending: true });
});
});
If I replace the before and after hooks with beforeEach and afterEach, since the tests are async, is it possible that the hook gets executed for a test and dumps out the collection before another test, which is using the collection, finishes executing? If so, what is a good way to go about populating a test database with fixtures for each test case?

How to close Express server inside Jest afterAll hook

I am trying to write integration tests for my Express server using Jest. Since Jest runs tests in parallel (and I would like to avoid running tests in sequence using --runInBand), I am using the get-port library to find a random available port so that different test suites don't have port collisions.
My tests all run successfully, the only problem is that the server is failing to close down properly inside the afterAll hook. This causes Jest to print the following in the console...
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
When I use the --detectOpenHandles flag, Jest just hangs after tests complete. Nothing gets printed to the console.
Here is my test code...
let axios = require('axios')
const getPort = require('get-port')
const { app } = require('../../index')
const { Todo } = require('../../models')
// set reference to server to access
// from within afterAll hook
let server
beforeAll(async () => {
const port = await getPort()
axios = axios.create({ baseURL: `http://localhost:${port}` })
server = app.listen(port)
})
afterAll(() => {
server.close()
})
describe('GET /todos', () => {
it('returns a list of todos', async () => {
const { data: todos } = await axios.get('/todos')
todos.forEach(todo => {
expect(Todo.validate(todo)).toEqual(true)
})
})
})
I am on that github thread on this issue. Here is exactly the configuration that works for me. In package.json
"test": "jest --no-cache --detectOpenHandles --runInBand --forceExit",
Here is the configuration in test file
afterEach(async () => {
await server.close();
});
afterAll(async () => {
await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});
beforeEach(() => {
// eslint-disable-next-line global-require
server = require('../index');
jest.setTimeout(30000);
});
OR you have only afterAll to set timeout and settimeout for each test in the test body individually.That's example below
afterEach(async () => {
await server.close();
});
afterAll(async () => {
await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});
beforeEach(() => {
// eslint-disable-next-line global-require
server = require('../index');
});
describe('POST /customers', () => {
jest.setTimeout(30000);
test('It creates a customer', async () => {
const r = Math.random()
.toString(36)
.substring(7);
const response = await request(server)
.post('/customers')
.query({
name: r,
email: `${r}#${r}.com`,
password: 'beautiful',
});
// console.log(response.body);
expect(response.body).toHaveProperty('customer');
expect(response.body).toHaveProperty('accessToken');
expect(response.statusCode).toBe(200);
});
});
The root cause is that the express app server is still running after the tests complete. So the solution is to close the server.
In the main server file:
export const server = app.listen(...)
In the test file:
import { server } from './main-server-file'
afterAll(() => {
server.close();
});
Using nodejs 17.4.0, jest 27.5.1, supertest 6.2.2. Running test with
cross-env NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest

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.

Resources