question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Jest: Connection not closing after test even with testingModule.close()

See original GitHub issue

I’m submitting a…


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

When using a database (mongodb-memory-server) in a test case, the test passes but runs indefinitely with Jest throwing the warnings:

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.

--detectOpenHandles supresses the warning but doesn’t show any info.

Expected behavior

The connection should be closed after all tests and the test case should complete after all tests pass, and there should be no async warning.

Minimal reproduction of the problem with instructions

test-database.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';

@Module({
  imports: [
    MongooseModule.forRootAsync({
      useFactory: async () => {
        const mongod = new MongoMemoryServer();
        return {
          uri: await mongod.getConnectionString(),
          useNewUrlParser: true,
          useUnifiedTopology: true,
          useCreateIndex: true,
        };
      },
    }),
  ],
})
export class TestDatabaseModule {}

test.service.ts

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';

@Injectable()
export class TestService {
  constructor(
    @InjectModel('test') private readonly test: Model<any>,
  ) {}
}

test.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { MongooseModule } from '@nestjs/mongoose';
import { TestDatabaseModule } from '../database/test-database.module';
import * as mongoose from 'mongoose';
import { TestService } from './test.service';

const testSchema = new mongoose.Schema({
  test: String,
});

describe('TestService', () => {
  let testingModule: TestingModule;
  let service: TestService;

  beforeEach(async () => {
    testingModule = await Test.createTestingModule({
      imports: [
        TestDatabaseModule,
        MongooseModule.forFeature([{ name: 'test', schema: testSchema }]),
      ],
      providers: [TestService],
    }).compile();

    service = testingModule.get<TestService>(TestService);
  });

  afterEach(async () => {
    await testingModule.close();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

jest.config.js

// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
/** @type {jest.DefaultOptions} */
module.exports = {
  // All imported modules in your tests should be mocked automatically
  // automock: false,

  // Stop running tests after `n` failures
  // bail: 0,

  // Respect "browser" field in package.json when resolving modules
  // browser: false,

  // The directory where Jest should store its cached dependency information
  // cacheDirectory: "C:\\Users\\mn\\AppData\\Local\\Temp\\jest",

  // Automatically clear mock calls and instances between every test
  clearMocks: true,

  // Indicates whether the coverage information should be collected while executing the test
  // collectCoverage: false,

  // An array of glob patterns indicating a set of files for which coverage information should be collected
  // collectCoverageFrom: null,

  // The directory where Jest should output its coverage files
  coverageDirectory: "coverage",

  // An array of regexp pattern strings used to skip coverage collection
  // coveragePathIgnorePatterns: [
  //   "\\\\node_modules\\\\"
  // ],

  // A list of reporter names that Jest uses when writing coverage reports
  // coverageReporters: [
  //   "json",
  //   "text",
  //   "lcov",
  //   "clover"
  // ],

  // An object that configures minimum threshold enforcement for coverage results
  // coverageThreshold: null,

  // A path to a custom dependency extractor
  // dependencyExtractor: null,

  // Make calling deprecated APIs throw helpful error messages
  // errorOnDeprecated: false,

  // Force coverage collection from ignored files using an array of glob patterns
  // forceCoverageMatch: [],

  // A path to a module which exports an async function that is triggered once before all test suites
  // globalSetup: null,

  // A path to a module which exports an async function that is triggered once after all test suites
  // globalTeardown: null,

  // A set of global variables that need to be available in all test environments
  // globals: {},

  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
  // maxWorkers: "50%",

  // An array of directory names to be searched recursively up from the requiring module's location
  // moduleDirectories: [
  //   "node_modules"
  // ],

  // An array of file extensions your modules use
  moduleFileExtensions: [
    "js",
    "json",
    "ts",
  ],

  // A map from regular expressions to module names that allow to stub out resources with a single module
  // moduleNameMapper: {},

  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
  // modulePathIgnorePatterns: [],

  // Activates notifications for test results
  // notify: false,

  // An enum that specifies notification mode. Requires { notify: true }
  // notifyMode: "failure-change",

  // A preset that is used as a base for Jest's configuration
  // preset: null,

  // Run tests from one or more projects
  // projects: null,

  // Use this configuration option to add custom reporters to Jest
  // reporters: undefined,

  // Automatically reset mock state between every test
  // resetMocks: false,

  // Reset the module registry before running each individual test
  // resetModules: false,

  // A path to a custom resolver
  // resolver: null,

  // Automatically restore mock state between every test
  // restoreMocks: false,

  // The root directory that Jest should scan for tests and modules within
  rootDir: 'src',

  // A list of paths to directories that Jest should use to search for files in
  // roots: [
  //   "<rootDir>"
  // ],

  // Allows you to use a custom runner instead of Jest's default test runner
  // runner: "jest-runner",

  // The paths to modules that run some code to configure or set up the testing environment before each test
  // setupFiles: [],

  // A list of paths to modules that run some code to configure or set up the testing framework before each test
  // setupFilesAfterEnv: [],

  // A list of paths to snapshot serializer modules Jest should use for snapshot testing
  // snapshotSerializers: [],

  // The test environment that will be used for testing
  testEnvironment: "node",

  // Options that will be passed to the testEnvironment
  // testEnvironmentOptions: {},

  // Adds a location field to test results
  // testLocationInResults: false,

  // The glob patterns Jest uses to detect test files
  // testMatch: [
  //   "**/__tests__/**/*.[jt]s?(x)",
  //   "**/?(*.)+(spec|test).[tj]s?(x)"
  // ],

  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
  // testPathIgnorePatterns: [
  //   "\\\\node_modules\\\\"
  // ],

  // The regexp pattern or array of patterns that Jest uses to detect test files
  testRegex: '.spec.ts$',

  // This option allows the use of a custom results processor
  // testResultsProcessor: null,

  // This option allows use of a custom test runner
  // testRunner: "jasmine2",

  // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
  // testURL: "http://localhost",

  // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
  // timers: "real",

  // A map from regular expressions to paths to transformers
  transform: {
    "^.+\\.(t|j)s$": "ts-jest"
  },

  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
  // transformIgnorePatterns: [
  //   "\\\\node_modules\\\\"
  // ],

  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
  // unmockedModulePathPatterns: undefined,

  // Indicates whether each individual test should be reported during the run
  // verbose: null,

  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
  // watchPathIgnorePatterns: [],

  // Whether to use watchman for file crawling
  // watchman: true,
};

What is the motivation / use case for changing the behavior?

N/A

Environment


Nest version: 6.7.2
@nestjs/mongoose version: 6.1.2

 
For Tooling issues:
- Node version: v12.13.0  
- Platform:  Windows

Others:

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

47reactions
VictorAssiscommented, May 30, 2020

I found a solution that works in my case. To Jest exits properly its necessary disconnect Mongoose and MongoMemoryServer.

db-test-module.ts

import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';

let mongod:MongoMemoryServer

export default (customOpts: MongooseModuleOptions = {}) => MongooseModule.forRootAsync({
  useFactory: async () => {
    mongod = new MongoMemoryServer();
    const uri = await mongod.getUri()
    return {
      uri,
      ...customOpts
    }
  }
})

export const closeMongoConnection = async () => {
  if (mongod) await mongod.stop()
}

test.service.spect.ts

import { Test, TestingModule } from '@nestjs/testing';
import { TestService } from './test.service';
import { MongooseModule, getConnectionToken } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
import { TestSchema } from '../models/test.model';
import DbModule, { closeMongoConnection } from '../../../test/utils/db-test.module';

describe('TestService', () => {
  let service: TestService;
  let connection: Connection;

  beforeEach(async () => {
    const module:TestingModule = await Test.createTestingModule({
      imports: [
        DbModule({
          connectionName: (new Date().getTime() * Math.random()).toString(16)
        }),
        MongooseModule.forFeature([
          { name: 'Test', schema: TestSchema }
        ])
      ],
      providers: [TestService]
    }).compile();

    service = module.get<TestService>(TestService);
    connection = await module.get(getConnectionToken());
  });

  afterEach(async () => {
    await connection.close()
    await closeMongoConnection()
  })

  it('should be defined', async () => {
    expect(service).toBeDefined();
  });
});
8reactions
jsdevtomcommented, Jan 7, 2021

Force closing worked for me with the regular MongoDB Server:

import { Test, TestingModule } from '@nestjs/testing';
import { TestService } from './test.service';
import { MongooseModule, getConnectionToken } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
import { TestSchema } from '../models/test.model';

describe('TestService', () => {
 let connection: Connection;

 beforeEach(async () => {
   const module:TestingModule = await Test.createTestingModule({
     imports: [
       AppModule,
     ],
   }).compile();

   connection = await module.get(getConnectionToken());
 });

 afterEach(async () => {
   await connection.close(/*force:*/ true); // <-- important
 });
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to properly close mongoose connection in jest test
By disconnecting before reconnecting to the database the error is gone. beforeAll(async () => { await mongoose.disconnect(); await mongoose.
Read more >
Testing | NestJS - A progressive Node.js framework
In the following example, we test two classes: CatsController and CatsService . As mentioned, Jest is provided as the default testing framework.
Read more >
Unit testing NestJS with mongo in memory...
Since I am a lazy brat, I do not want to rewrite the in-memory mongod ... beforeEach(async () => { const module: TestingModule...
Read more >
How to Test Your Express.js and Mongoose Apps with Jest ...
In this article, I'll show you how to write tests for your ... MONGODB_URI); }); /* Closing database connection after each test.
Read more >
End-to-end testing in NestJS with TypeORM - LogRocket Blog
NestJS comes with Jest and Supertest out of the box for creating unit tests. We'll explore the thought process behind test-driven development ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Hashnode Post

No results found