簡體   English   中英

Express/Typescript + Jest + Supertest + TypeORM - 隨機超時異常和 Memory 泄漏

[英]Express/Typescript + Jest + Supertest + TypeORM - Random Timeout Exception & Memory Leak

我正在嘗試為快遞 API 編寫單元測試。每個測試套件在測試之前創建一個快遞服務器的新實例。

當每個測試套件獨立運行時似乎沒有問題,當一起運行時我看到一些問題。

9 個測試套件完成后,每個單獨的測試似乎 output 以下警告:

(node:5282) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 connection listeners added to [Server]. Use emitter.setMaxListeners() to increase limit
    at _addListener (node:events:453:17)
    at Server.addListener (node:events:469:10)
    at Server.<anonymous> (node_modules/async-listener/index.js:90:10) => this.on('connection', function (socket) {...
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23) => return original.apply(this, arguments);
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)
    at Server.<anonymous> (node_modules/async-listener/index.js:97:23)

我對僅抑制此警告不感興趣,特別是如果這與 memory 泄漏有關,所以請不要建議這樣做。

一些隨機(似乎)測試有時會因錯誤而失敗:

thrown: "Exceeded timeout of 20000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

我確信這與指定的超時值無關,因為這些測試將在不到 500 毫秒的時間內成功,並且無論超時值設置為多少,測試都會失敗。

Example.test.ts 文件

import Server from '../../server';
import { generateTestData } from '../generate-database-data';

import * as request from 'supertest';
import { APIRoutes } from '../../routes';

const server = new Server(); // New instance of express server

describe('API Test', () => {

    // Sets up TypeORM DB connection
    beforeAll(async() => await server.connect());

    // Run raw sql statements using typeorm query runner to truncate and populate fresh data for each test case
    beforeEach(async () => await generateTestData(server.connection));

    // Close TypeORM connection after all tests
    afterAll(async () => await server.connection.close());
    
    test('Get All', async () => {
        const response = await request(server.app)
          .get(APIRoutes.ROUTE)
          .set('Accept', 'application/json');

        expect(response.status).toEqual(200);
        expect(response.body.length).toEqual(4);
    });

    ...

});

服務器.ts

import * as express from 'express';
import 'express-async-errors';
import { createConnection } from 'typeorm';
import { configureRoutes } from './routes';

require('dotenv').config({path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env'});

class Server {

    public app: express.Application;
    public server;
    public connection;

    constructor() {
        this.app = express();
        this.configuration();
    }

    public configuration() {
        this.app.set('port', process.env.PORT);
        this.app.use(express.json());
    }

    public async connect() {
        this.connection = await createConnection({
            'type': 'mysql',
            'host': process.env.DB_HOST,
            'port': parseInt(process.env.DB_PORT),
            'username': process.env.DB_USERNAME,
            'password': process.env.DB_PASSWORD,
            'database': process.env.DB_DATABASE,
            'dropSchema': process.env.DB_DROP_SCHEMA === 'true',
            'synchronize': true,
            'logging': ['error'],
            'logger': 'file',
            'entities': process.env.NODE_ENV === 'test' ? ['src/entities/**/*.ts'] : ['build/src/entities/**/*.js'],
            'cli': { 'entitiesDir': 'build/src/entities' },
            'extra': process.env.NODE_ENV === 'test' ? {
                'connectionLimit': 0
            } : {},
        });
        configureRoutes(this.app);
        return
    }

    public async start() {
        await this.connect();
        if (process.env.NODE_ENV !== 'test') {
            this.server = this.app.listen(process.env.PORT, () => {
                console.log(`App listening on the port ${process.env.PORT}`);
            });
        }
    }

    public async stop() {
        await this.server?.close();
        await this.connection?.close();
    }
}

// start express server
if(process.env.NODE_ENV !== 'test') {
    const server = new Server();
    server.start();
}

export default Server;

相關依賴

 "devDependencies": {
    "@babel/core": "^7.17.8",
    "@babel/preset-env": "^7.16.11",
    "@types/express": "^4.17.13",
    "@types/jest": "^27.0.1",
    "@types/node": "^17.0.18",
    "@types/supertest": "^2.0.12",
    "babel-jest": "^27.5.1",
    "jest": "^27.2.0",
    "jest-express": "^1.12.0",
    "supertest": "^6.1.6",
    "ts-jest": "^27.0.5",
    "ts-node": "^10.5.0",
    "typescript": "^4.5.5"
  },
  "dependencies": {
    "dotenv": "^8.6.0",
    "express": "^4.17.2",
    "express-async-errors": "^3.1.1",
    "helmet": "^5.0.2",
    "typeorm": "^0.2.42"
  }

我不認為這是與 typeorm 和數據庫連接相關的問題,因為我可以刪除連接以及生成測試數據的查詢,並且一些測試仍然會失敗並出現相同的超時錯誤和警告消息。 根據at Server.addListener (node:events:469:10)這提示我相信此錯誤與快速服務器或超級測試有關。

更新

這 2 個問題似乎沒有關聯 - 我相信我已經找到了超時錯誤問題的解決方案。

這個問題似乎是數據庫連接是如何通過 typeorm 建立的結果。

我從 express 服務器中刪除了數據庫連接邏輯,並在setupFilesAfterEnv文件中對其進行了獨立初始化。 beforeAll function 如果當前連接不存在,我創建連接。 如果存在當前連接,我將調用.connect()方法。

我有一個腳本正在運行以無限期地運行測試,在過去一小時內沒有出現任何錯誤情況,因此我相信問題已得到解決。

但是,我仍然看到MaxListenersExceededWarning警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM