Multiple connections with same name are created in e2e test of NestJs with in memory database - testing

I have NestJs application with TypeORM configured with mysql. I want to have e2e(integration) test and for that reason I want to have in memory database in the tests which I configured this way:
{
type: 'sqlite',
database: ':memory:',
synchronize: true,
dropSchema: true,
entities: [`dist/**/*.entity{.ts,.js}`],
}
And the setup of the tests
beforeEach(async () => {
const moduleFixture: TestingModule =
await Test.createTestingModule({imports: [AppModule, UserModule]})
.overrideProvider(TypeOrmConfigService).useClass(MockTypeOrmConfigService)
.compile();
app = await moduleFixture.createNestApplication();
await app.init();
});
. When running the test I got
AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.
at new AlreadyHasActiveConnectionError (/Users/user/workspace/app/src/error/AlreadyHasActiveConnectionError.ts:8:9)
at ConnectionManager.Object.<anonymous>.ConnectionManager.create (/Users/user/workspace/app/src/connection/ConnectionManager.ts:57:23)
at Object.<anonymous> (/Users/user/workspace/app/src/index.ts:228:35)
at step (/Users/user/workspace/app/node_modules/tslib/tslib.js:136:27)
at Object.next (/Users/user/workspace/app/node_modules/tslib/tslib.js:117:57)
at /Users/user/workspace/app/node_modules/tslib/tslib.js:110:75
at new Promise (<anonymous>)
at Object.__awaiter (/Users/user/workspace/app/node_modules/tslib/tslib.js:106:16)
at Object.createConnection (/Users/user/workspace/app/node_modules/typeorm/index.js:186:20)
at rxjs_1.defer (/Users/user/workspace/app/node_modules/#nestjs/typeorm/dist/typeorm-core.module.js:151:29)
(node:19140) UnhandledPromiseRejectionWarning: AlreadyHasActiveConnectionError: Caught error after test environment was torn down
If I move the setup from beforeEach in beforeAll block it's ok, but I'm afraid that when I create several specs the error will come back. How should be handled properly?
EDIT:
The problem was that each test is making a setup of the application and so creates a new connection.The solution was to use "keepConnectionAlive: true," in order all tests to reuse same connection.

keepCOnnectionAlive: true is the way to go

Using keepConnectionAlive: true produced the following error for me.
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.
Adding the below to each e2e test fixed my issue:
afterEach(async () => {
await app.close();
});

Base on 0xCAP's answer, you can do something like this also.
// jest.setup.ts
jest.mock("/path/to/database/config/object", () => {
const { databaseConfig, ...rest } = jest.requireActual("/path/to/database/config/object")
return {
...rest,
databaseConfig: {
...databaseConfig,
keepConnectionAlive: true // replace old config
}
}
})
// jest.config.js
module.exports = {
...other options
setupFilesAfterEnv: ["jest.setup.ts"],
}

Related

Mongoose connection closing too soon before tests have run

I am a beginner using jest to test a node/express app with mongo database.
I am getting an issue where different tests are failing each time I run the tests and sometimes they all pass/all fail. I think it is because of a time-out or things not happening in the right order because I'm getting this error:
MongoPoolClosedError: Attempted to check out a connection from closed connection pool
a) can you let me know if you think I'm on the right track?
b) if so, is the solution to make this into an async function and how can I do that? (I have tried making it into async await, using .then and also putting the code that clears the database collections into the test files instead of the helper and have had no success so far.
beforeAll( (done) => {
mongoose.connect("mongodb://127.0.0.1/jobBuddy_test", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
var db = mongoose.connection;
const users = db.collection('users')
users.deleteMany({})
const applications = db.collection('applications')
applications.deleteMany({})
db.on("error", console.error.bind(console, "MongoDB connection error:"));
db.on("open", function () {
done();
});
});
afterAll(function (done) {
mongoose.connection.close(true, function () {
done();
});
});

Detox with Split.IO

I found a jest mock for split.io for react-native. I am now trying to use this mock so that I do not receive network timeouts because split.io is trying to sync in the background. Here is the mock:
jest.mock('#splitsoftware/splitio-react-native', () => {
const splitio = jest.requireActual('#splitsoftware/splitio-react-native');
return {
...splitio,
SplitFactory: () => {
return splitio.SplitFactory({
core: {
authorizationKey: 'localhost',
},
// Mock your splits and treatments here
features: {},
sync: {
localhostMode: splitio.LocalhostFromObject(),
},
});
},
};
});
I currently put this in my detox init.js file, but it doesn't seem to be doing anything. The only way, so far, I have been able to get my tests to run is to just immediately destroy my SplitFactory as soon as I create it (not through the mock). Obviously, this isn't ideal since I'd have to change the code every time I wanted to run it. I tried creating a .mock.ts file, but that also didn't get read, and when I tried to adjust my metro.config.js, it just failed to run at all. Does anyone have any ideas of how I can get this to run properly in detox for iOS, or have experience with this?
I had the same issue with split.io and detox, when a particular split.io would block my tests indefinitely. The only work around i found was
await device.disableSynchronization();
found here

Close redis connection when using NestJS Queues

I'm trying to setup E2E tests in a NestJS project, however, jest output looks like this:
Jest did not exit one second after the test run has completed
After a lot of reading this is because there are some resources, not yet liberated, after some debugging it turns there's an open connection to redis created by ioredis which is used by bull which is used by NestJS to do task queue processing. The thing is that I don't have a reference to the connection in the test code, so how can I close it? I'm tearing down the Nest application in the afterAll jest's hook like this:
afterAll(async () => {
await app.close();
});
but it does nothing, the connection is still there and the jest error message persists. I know I can just add the --forceExit to the jest command but that is not solving anything, it's just hiding the problem under the rug.
This took me awhile to figure out. You need to close the module in the afterAll hook. I was able to find this from looking at the tests in the nestJS Bull repo.
describe('RedisTest', () => {
let module: TestingModule;
beforeAll(async () => {
module = Test.createTestingModule({
imports: [
BullModule.registerQueueAsync({
name: 'test2',
}),
],
});
});
afterAll(async () => {
await module.close();
});
});
https://github.com/nestjs/bull/blob/master/e2e/module.e2e-spec.ts
After struggling almost to getting depressed i found a solution that worked for me.
I'm using "#nestjs/bull": "^0.3.1", "bull": "^3.21.1".
because the queue from bull package uses redis, it keeps the connection open although the module & app are closed.
await moduleRef.close();
await app.close();
i realized that when using --detectOpenHandles while relying on leaked-handles library for more information, you will see something like this in the console:
tcp stream {
fd: 20,
readable: true,
writable: false,
address: {},
serverAddr: null
}
tcp handle leaked at one of:
at /media/user/somePartitionName/Workspace/Nest/project-
name/node_modules/ioredis/built/connectors/StandaloneConnector.js:58:45
tcp stream {
fd: 22,
readable: true,
writable: true,
address: { address: '127.0.0.1', family: 'IPv4', port: 34876 },
serverAddr: null
}
Solution
using beforEach() & afterEach()
in beforEach(), add this instruction to get an instance of your queue :
queue = moduleRef.get(getQueueToken("queuename"));
in afterEach(), close the queue like so: (also close your app & module for better practice)
await queue.close();
Note
using beforAll() & afterAll() doesn't work and the same problem occurs, at least from what i have tried, both beforEach() & afterEach() work combined.
You don't have to add queue.close() to every test, just close queues in their own service/provider using OnModuleDestroy Hook:
#Injectable()
export class ServicesConsumer implements OnModuleDestroy {
constructor(
#InjectQueue('services')
private readonly servicesQueue: Queue,
) { }
async onModuleDestroy() {
await this.servicesQueue.close();
}

Running Knex Migrations Between Mocha Tests

I was using Mocha to test my Nodejs app with a test database. In order to reset the DB before each test I had the following code, which worked perfectly:
process.env.NODE_ENV = 'test';
var knex = require('../db/knex');
describe("Add Item", function() {
beforeEach(function(done) {
knex.migrate.rollback()
.then(function() {
knex.migrate.latest()
.then(function() {
return knex.seed.run()
.then(function() {
done();
});
});
});
});
...
I've since switched from mocha to mocha-casperjs for my integration tests, and now the knex migrations won't run. I'm given this error message with the exact same before each hook:
undefined is not an object (evaluating 'knex.migrate.rollback')
phantomjs://platform/new-item.js:12:17
value#phantomjs://platform/mocha-casperjs.js:114:20
callFnAsync#phantomjs://platform/mocha.js:4314:12
run#phantomjs://platform/mocha.js:4266:18
next#phantomjs://platform/mocha.js:4630:13
phantomjs://platform/mocha.js:4652:9
timeslice#phantomjs://platform/mocha.js:12620:27
I'm pretty sure that migration functionality is not included in webpack build. If you go to http://knexjs.org/ open up debug console and checkout different clients e.g. mysql.migrate you see that there are no functions declared at all.
Actually you can check it out with node too if you explicitly load webpack build instead of node lib.
// load webpack build instead of node build...
let knex = require('knex/build/knex')({client : 'pg'});
console.log(knex.migrate);
// outputs: {}
So... the question is why are you trying to run your tests on PhantomJS browser instead of node.js?

mocha 'after' fails saying it can't find 'app'

Ok, my mocha tests will pass if I comment out the 'before' and 'after' methods. I am sure that both of my errors are related to each other.
The 'after' method fails stating app.close isn't a function. The 'before' method fails saying it cant find 'app' on my line 7 (clearing server cache).
I am completely out of options or ideas. I would like to be able to start and stop my server at my command. This is the first time that I have attempted to include any type of 'before/after' methods to my mocha testing. working code below, but with my failing portion commented out. Any suggestions??
var request = require('supertest');
var app = require('../../server');
describe('server', function() {
before(function () {
//var app = require('../../server')();
//delete require.cache[require.resolve('app')];
});
after(function () {
//app.close();
});
describe('basic comms', function() {
it('responds to root route', function testSlash(done) {
request(app)
.get('/')
.expect('Content-type', /json/)
//.expect(res.message).to.equal('Hello World!')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
//console.log('testing 404 response');
request(app)
.get('/foo/bar')
.expect(404, done);
});
});
});
In before you require your app in a different way than in line 2. Why would you not use already required app?
Example:
before(function () {
// here you can use app from line 2
});
Regarding app.close, where did you find this function?
Check Express docs:
http://expressjs.com/en/4x/api.html#app
To close express server, you can use this approach:
how to properly close node-express server?