jest + supertest async callback was not invoked - node.js

I finished creating my express server and I'm using Jest + Supertest to test my endpoints and testing all the CRUD operations for my mongoose db. So far I can't even create a new user in the test.
Code:
//server.spec.js
const supertest = require('supertest');
const server = require('./server');
describe('server.js', () => {
describe('GET /', () => {
it('should connect to server responding w/ 200', (done) => {
return supertest(server)
.get('/')
.then(res => {
expect(res.status).toBe(200);
done();
});
});
});
describe('/api/users/', () => {
it('should create a new user', async () => {
return await supertest(server)
.post('/api/users/register')
.send({ username: "jest", email: "jest#jest.com", password: "jestjest" })
.set("Accept", "application/json")
.then(res => {
console.log(res.body)
expect(res.status).toBe(201)
})
})
});
});
Console output:
FAIL src/server.spec.js (8.573 s)
server.js
GET /
√ should connect to server responding w/ 200 (231 ms)
/api/users/
× should create a new user (5005 ms)
● server.js › /api/users/ › should create a new user
: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
17 | it('should create a new user', async () => {
18 | return await supertest(server)
> 19 | .post('/api/users/register')
| ^
20 | .send({ username: "jest", email: "jest#jest.com", password: "jestjest" })
21 | .set("Accept", "application/json")
22 | .then(res => {
at new Spec (node_modules/jest-jasmine2/build/jasmine/Spec.js:116:22)
at Suite.describe (src/server.spec.js:19:9)
at Suite.Object.<anonymous>.describe (src/server.spec.js:18:5)
at Object.<anonymous> (src/server.spec.js:6:1)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 8.61 s, estimated 9 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.(node:26872) UnhandledPromiseRejectionWarning: Error: Caught error after test environment was torn down
I've attempted different types of solutions but to no success.
## EDIT ##
Thank you to #Estus Flask for providing me the answer.
I forgot to add my database.

Related

Express.js + Jest + Supertest + TypeORM ReferenceError

I am trying to develop an API and test it with jest + supertest + typeorm. So, if I import my server instance, I see this error:
ReferenceError: You are trying to "import" a file after the Jest environment has been torn down. From test/Test.test.ts.
// src/app.ts
import express from "express";
import ds from "./app-data-source";
ds.initialize().catch((err) => {
console.error("Error during Data Source initialization:", err)
})
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.on("close", () => ds.destroy())
export default app;
// src/server.ts
import app from "./app";
const server = app.listen(3000);
export default server;
// src/app-data-source.ts
import { DataSource } from "typeorm"
const ds = new DataSource({
type: "sqlite",
database: ":memory:",
entities: ["src/entity/*.ts"],
logging: false,
synchronize: true,
})
export default ds;
// test/Test.test.ts
import request from "supertest";
import server from "../src/server";
describe("GET / - a simple api endpoint", () => {
test("Test #1", async () => {
const response = await request(server).get("/");
expect(response.statusCode).toBe(404);
});
afterAll(done => {
server.close(done)
})
});
And as a result I get this error
yarn run v1.22.17
warning ../package.json: No license field
$ NODE_ENV=test jest --runInBand
PASS test/Test.test.ts
GET / - a simple api endpoint
✓ Test #1 (9 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.134 s, estimated 2 s
Ran all test suites.
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From test/Test.test.ts.
at tryToRequire (node_modules/src/util/ImportUtils.ts:21:17)
at importOrRequireFile (node_modules/src/util/ImportUtils.ts:35:25)
at async /Users/sergey/Work/*****/node_modules/src/util/DirectoryExportedClassesLoader.ts:57:45
at async Promise.all (index 0)
at async importClassesFromDirectories (node_modules/src/util/DirectoryExportedClassesLoader.ts:63:18)
at async ConnectionMetadataBuilder.buildEntityMetadatas (node_modules/src/connection/ConnectionMetadataBuilder.ts:92:17)
at async DataSource.buildMetadatas (node_modules/src/data-source/DataSource.ts:674:13)
at async DataSource.initialize (node_modules/src/data-source/DataSource.ts:242:13)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
I figured out that this is a problem, probably due to an open typeorm connection, but I don't know how to fix it because I do
// In test file
// ...
afterAll(done => {
server.close(done)
})
// ...
as well as
import ds from "./app-data-source";
ds.initialize() // ...
// ...
app.on("close", () => ds.destroy())
I tried to search for information on Google and did not find anything useful, all the answers do not help me, useFakeTimers not working for me

Cannot run test API call using Jest / mongodb-memory-server (problem setimeout)

I did test many functions in my app using jest / mongodb-memory-server and they all works great. But but some reason I cannot run API call test.
Every time I'm trying to run a api call I have a setimout problem and even if I increase it it still does not work. My functions just return a list of users... no more.
FAIL Tests/Controller/auth.test.js (11.991 s)
● Console
console.warn
Using NodeJS below 12.22.0
at Object.<anonymous> (node_modules/mongodb-memory-server-core/src/util/MongoInstance.ts:21:11)
at Object.<anonymous> (node_modules/mongodb-memory-server-core/src/MongoMemoryServer.ts:13:1)
● test updateSchemaField() › get users list
MongooseError: Can't call `openUri()` on an active connection with different connection strings. Make sure you aren't calling `mongoose.connect()` multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections
10 | useUnifiedTopology: true,
11 | };
> 12 | await mongoose.connect(uri, mongooseOpts);
| ^
13 | };
14 |
15 | module.exports.closeDatabase = async () => {
at NativeConnection.Object.<anonymous>.Connection.openUri (node_modules/mongoose/lib/connection.js:697:13)
at node_modules/mongoose/lib/index.js:330:10
at node_modules/mongoose/lib/helpers/promiseOrCallback.js:32:5
at promiseOrCallback (node_modules/mongoose/lib/helpers/promiseOrCallback.js:31:10)
at Mongoose.Object.<anonymous>.Mongoose._promiseOrCallback (node_modules/mongoose/lib/index.js:1151:10)
at Mongoose.connect (node_modules/mongoose/lib/index.js:329:20)
at Object.<anonymous>.module.exports.connect (Tests/db.js:12:18)
at Tests/Controller/auth.test.js:5:23
● test updateSchemaField() › get users list
thrown: "Exceeded timeout of 5000 ms for a hook.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
5 | beforeAll(async () => await db.connect());
6 |
> 7 | afterEach(async () => await db.clearDatabase());
| ^
8 |
9 | afterAll(async () => await db.closeDatabase());
10 |
at Object.<anonymous> (Tests/Controller/auth.test.js:7:1)
● Test suite failed to run
thrown: "Exceeded timeout of 5000 ms for a hook.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
7 | afterEach(async () => await db.clearDatabase());
8 |
> 9 | afterAll(async () => await db.closeDatabase());
| ^
10 |
11 | describe("test updateSchemaField()", (done) => {
12 | it("get users list", async () => {
at Object.<anonymous> (Tests/Controller/auth.test.js:9:1)
PASS Tests/Data/schemaCRUD.test.js
● Console
console.warn
Using NodeJS below 12.22.0
at Object.<anonymous> (node_modules/mongodb-memory-server-core/src/util/MongoInstance.ts:21:11)
at Object.<anonymous> (node_modules/mongodb-memory-server-core/src/MongoMemoryServer.ts:13:1)
PASS Tests/Utils/user.test.js
Test Suites: 1 failed, 2 passed, 3 total
Tests: 1 failed, 9 passed, 10 total
Snapshots: 0 total
Time: 12.859 s, estimated 13 s
Ran all test suites matching /.\/Tests/i.
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.
The test
const request = require("supertest");
const app = require("../../app");
const db = require("../db");
beforeAll(async () => await db.connect());
afterEach(async () => await db.clearDatabase());
afterAll(async () => await db.closeDatabase());
describe("test updateSchemaField()", () => {
it("get users list", async () => {
const response = await request(app).get("/api/auth/test");
console.log(response);
});
});
The script is trying to connect with multiple URI. You are establishing a database connection in the test file is fine but there is also another database connection call with different URI in app.js .

How can I return the response from my api when testing with chai-http and Jest

I am trying to test my node api index endpoint with the code below
index.test.js
const chai = require('chai')
const chaiHttp = require('chai-http')
const server = 'http://localhost:8000'
chai.use(chaiHttp)
describe('set up test', () => {
it('set up test', () => {
expect(1).toEqual(1)
})
})
describe('index route test', () => {
it('index route test', (done) => {
const { res } = chai
.request(server)
.get('/')
.end((res) => {
expect(res.statusCode).toBe(200)
done()
})
console.log(res)
})
})
My fail test for 'index route test' passes yet it's supposed to fail(SOLVED).
The Received response is undefined(which I logged in the console).
My output is as below:
Output
> jest --forceExit || true
FAIL tests/index.test.js
set up test
✓ set up test (6 ms)
index route test
✕ gives welcome message (49 ms)
● index route test › gives welcome message
expect(received).toBe(expected) // Object.is equality
Expected: 200
Received: undefined
18 | .get('/')
19 | .end((res) => {
> 20 | expect(res.statusCode).toBe(200)
| ^
21 | done()
22 | })
23 | console.log(res)
at tests/index.test.js:20:32
at Test.Object.<anonymous>.Request.callback (node_modules/superagent/lib/node/index.js:728:3)
at ClientRequest.<anonymous> (node_modules/superagent/lib/node/index.js:647:10)
console.log
undefined
at Object.<anonymous> (tests/index.test.js:23:13)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 1.63 s, estimated 2 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?
How can I return the response?
I wasn't getting any response by passing the app url to the server variable. So I changed my syntax to ES6 import instead of the require and used the app module rather than the url for the application server as in the chai-http documentation here
So my code structure transitioned to
import chai from 'chai'
import chaiHttp from 'chai-http'
import server from '../app'
chai.use(chaiHttp)
describe('set up test', () => {
it('set up test', () => {
expect(1).toEqual(1)
})
})
describe('index route test', () => {
it('gives welcome message', (done) => {
chai
.request(server)
.get('/')
.then((res) => {
expect(res.statusCode).toBe(200)
done()
})
})
})
This way, I can capture the response to run checks on it. And my tests pass and required. The output is:
> jest --forceExit || true
PASS tests/index.test.js
set up test
✓ set up test (4 ms)
index route test
✓ gives welcome message (77 ms)
console.log
Application Server is up and running on port 8000
at Server.<anonymous> (app.js:43:11)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.189 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

Node e2e tests - async beforeEach/afterEach hooks fail on Windows

On OSX and Linux, the following works great (this is as simplified a reproduction as possible, hopefully without sacrificing meaning):
import { expect } from 'chai';
import { MongoClient, Db } from 'mongodb';
import { Application, Request } from 'express';
import { Server } from 'http';
import * as config from 'config';
describe('some test', () =>
{
let Session:{ new(app:Application):Request } = require('supertest-session'),
app:Application,
server:Server,
mongoClient:MongoClient,
db:Db;
beforeEach(async () =>
{
app = express();
server = app.listen(config.Http.port);
request = new Session(app);
// On Windows tests are executed before this resolves
mongoClient = await MongoClient.connect(config.Database.connectionOptions.url);
db = mongoClient.db(config.Database.connectionOptions.database);
});
afterEach(async () =>
{
await db.dropDatabase();
request.destroy();
server.close();
});
it('works like it oughtta', () =>
{
request.post('/api/account/login')
.send({ email: 'me#example.com', password: 'password' })
.expect(200)
.then((res) =>
{
expect(res.success).to.eq(true);
})
})
});
On a Windows machine, the above fails with the following output from npm:
13 verbose stack Exit status 4
13 verbose stack at EventEmitter.<anonymous> (C:\Path\To\AppData\Roaming\nvm\v9.4.0\node_modules\npm\node_modules\npm-lifecycle\lib\index.js:285:16)
... rest of stack
13 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:220:5)
If I take the database connection out of the beforeEach hook and do this instead, the test will run and pass, but I still notice hard-to-track-down failures in further tests with async hooks:
before(function(done)
{
MongoClient.connect(function(err, client)
{
mongoClient = client;
done();
});
});
after(function(done)
{
mongoClient.close(function() { done(); });
});
I've seen this behavior using Mocha, Jest and FuseBox test runners. Running node#9.4 on both machines. The solution to this cannot be "just make sure I test my tests on a Windows machine before I push".

Integration tests sporadically timeout on Heroku hobby-dev

Since we do not get any helpful support from help.heroku.com we make the last try here.
We are developping a classic web app consisting of:
----- ----- ----
|WEB| <----> |API| <----> |DB|
----- ----- ----
We currently are working with the following Heroku Dynos/Datastores
Heroku Postgres: Hobby Basic
Heroku API Dyno: Hobby
Heroku WEB Dyno: Hobby
The tech stack is:
runtime: nodejs (4.4.0)
db: postgres (9.6.1)
testframework: jasminejs (2.5.1)
query builder: knexjs (0.10.0)
We recently moved to from self hosted docker environment to Heroku and configured the Herokus CI pipeline which works fine for unit testing - but not integration testing.
The tests sporadically fails with timeouts (in average every 3rd test of the same commit). This is not stable enough to build up CI/CD.
Here the error messages we get:
**************************************************
* Failures *
**************************************************
1) integration test collections repository create() should return AUTHENTICATION_REQUIRED if user is anonymous
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
2) integration test collections repository create() should return AUTHORIZATION_REQUIRED if user is not space MEMBER
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
3) integration test collections repository create() should return collection if user is space MEMBER
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
For testing purposes we configured knex connection pooling to use one connection only:
export default {
client: 'pg',
connection: DB_URL,
pool: {
max: 1,
min: 1
}
};
The typical integration test setup is:
describe('integration test', () => {
describe('collections repository create()', () => {
//////////////////////////////////////////////////////////////////////
//Before the test block is execute all fixtures are loaded into the DB
//////////////////////////////////////////////////////////////////////
beforeAll((callback) => {
seed(path.join(__dirname, 'fixtures'))
.then(callback)
.catch((error) => {
fail(error);
callback();
});
});
////////////////////////////////////////////////////////////////////////
//Truncate all data from the DB before the next test block gets executed
////////////////////////////////////////////////////////////////////////
afterAll(resetDB);
it('should return AUTHENTICATION_REQUIRED if user is anonymous', (callback) => {
testUtils.testAsync({
callback,
catchTest: (error) => {
expect(error).toEqual(jasmine.any(repositoryErrors.AUTHENTICATION_REQUIRED));
},
runAsync: create({ props: { space: { id: 1 } } })
});
});
it('should return AUTHORIZATION_REQUIRED if user is not space MEMBER', (callback) => {
testUtils.testAsync({
callback,
catchTest: (error) => {
expect(error).toEqual(jasmine.any(repositoryErrors.AUTHORIZATION_REQUIRED));
},
runAsync: create({ props: { space: { id: 1 } }, userId: 1 })
});
});
it('should return collection if user is space MEMBER', (callback) => {
testUtils.testAsync({
callback,
runAsync: create({ props: { space: { id: 1 } }, userId: 2 }),
thenTest: (outcome) => {
expect(outcome).toEqual({ id: '1' });
}
});
});
...
seed:
const tableOrder = [
'users',
'guidelines',
'collections',
'spaces',
'details',
'files',
'guidelinesAuthors',
'collectionsUsers',
'spacesUsers',
'guidelinesCollections',
'spacesCollections',
'guidelinesDetails',
'guidelinesFiles',
'comments',
'commentsReplies',
'notifications'
];
export default function seed(path) {
return db.raw('BEGIN;')
.then(() => {
return tableOrder.reduce((promise, table) => {
return promise
.then(() => slurp({ path, table }))
.then(() => {
updateIdSequence(table)
.catch((error) => {
// eslint-disable-next-line no-console
console.log(
`Updating id sequence for table '${table}' failed! Error: `,
error.message
);
});
});
}, Promise.resolve()).then(() => db.raw('COMMIT;'));
})
.catch((error) => {
// eslint-disable-next-line no-console
console.log('SEED DATA FAILED', error);
return db.raw('ROLLBACK;');
});
}
...
resetDB:
export default function resetDB(callback) {
const sql = 'BEGIN; '
+ 'SELECT truncateAllData(); '
+ 'SELECT restartSequences(); '
+ 'COMMIT;';
return db.raw(sql)
.then(callback)
.catch((error) => {
// eslint-disable-next-line no-console
console.log('TRUNCATE TABLES FAILED', error);
return db.raw('ROLLBACK;');
});
}
Until now, these test have been running on local machines (Linux/Mac) and Codeship without any problem.
After almost two weeks of trying to get this work we made zero progress on this issue. I can't see anything wrong with this configuration and I start to belive Heroku has a serious issue with the datastores...
Has anybody experienced similar issues on Heroku?
Any idea what we can try else to get this work?

Resources