Problem:

当运行JEST和SuperTest时,在它到达我所描述的实际测试之前,我得到了一个错误.服务器使用启动脚本运行良好,应用程序已定义.但是在运行测试脚本时,APP是未定义的.

Background:

  • 我对打字相当陌生,这是我第一次使用任何类型的测试.
  • 正如在几篇博客文章和教程中看到的那样,我希望分离服务器实例,因为我计划在不同的文件中运行多个测试.

如果你有任何建议,即使是我已经try 过的,我会再试一次,然后告诉你.我无计可施,所以任何帮助都是非常可取的.谢谢.

Error:

 FAIL  src/components/users/user.test.ts
  ● Test suite failed to run

    TypeError: Cannot read properties of undefined (reading 'listen')

       6 |
       7 | dbConnection();
    >  8 | export const server = app.listen(config.server.port, () => {
         |                           ^
       9 |     logger.info(`Server is running on port: ${config.server.port}`);
      10 | });
      11 |

      at Object.<anonymous> (src/index.ts:8:27)
      at Object.<anonymous> (src/library/exitHandler/exitHandler.ts:2:1)
      at Object.<anonymous> (src/library/errorHandler/errorHandler.ts:2:1)
      at Object.<anonymous> (src/middleware/validateSchema.ts:3:1)
      at Object.<anonymous> (src/components/users/routes.ts:2:1)
      at Object.<anonymous> (src/server.ts:2:1)
      at Object.<anonymous> (src/components/users/user.test.ts:2:1)

user.test.ts

import request from 'supertest';
import app from '../../server';

describe('User registration', () => {
    it('POST /register --> return new user instance', async () => {
        await request(app)           // error occurs when reaching this point
            .post('/user/register')
            .send({
                firstName: 'Thomas',
                lastName: 'Haek',
                email: 'thomashaek@gmail.com',
                password: '12345678aA',
                confirmPassword: '12345678aA'
            })
            .expect(201)
            .then((response) => {
                expect(response.body).toEqual(
                    expect.objectContaining({
                        _id: expect.any(String),
                        firstName: expect.any(String),
                        lastName: expect.any(String),
                        email: expect.any(String),
                        token: expect.any(String)
                    })
                );
            });
    });
});

server.ts

import express, { Application } from 'express';
import userRouter from './components/users/routes';
import { routeErrorHandler } from './middleware/errorHandler';
import httpLogger from './middleware/httpLogger';
import './process';

const app: Application = express();

app.use(httpLogger);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/user', userRouter);
app.use(routeErrorHandler);

export default app

index.ts

import { createHttpTerminator } from 'http-terminator';
import config from './config/config';
import dbConnection from './config/dbConnection';
import logger from './library/logger';
import app from './server'

dbConnection();
export const server = app.listen(config.server.port, () => {
    logger.info(`Server is running on port: ${config.server.port}`);
});

export const httpTerminator = createHttpTerminator({ server });

package.json scripts

"scripts": {
    "test": "env-cmd -f ./src/config/test.env jest --watchAll",
    "start": "env-cmd -f ./src/config/dev.env node build/index.js",

  },

tsconfig.json

{
    "compilerOptions": {
        "outDir": "./build",
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true,
        "module": "commonjs",
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "target": "es6",
        "noImplicitAny": true,
        "moduleResolution": "node",
        "sourceMap": true,
    },
    "include": ["src/**/*"]
}

jest.config.ts

import { Config } from 'jest';

/** @type {import('ts-jest').JestConfigWithTsJest} */
const config: Config = {
    preset: 'ts-jest',
    testEnvironment: 'node',
    roots: ['./src'],
    moduleFileExtensions: ['js', 'ts'],
    clearMocks: true,
    collectCoverage: true,
    coverageDirectory: 'coverage',
    coveragePathIgnorePatterns: ['/node_modules/', '/src/config/'],
    coverageProvider: 'v8',
    coverageReporters: ['json', 'text', 'lcov', 'clover'],
    verbose: true
};

export default config;

exitHandler.ts

import mongoose from 'mongoose';
import { httpTerminator, server } from '../..';


import logger from '../logger';

class ExitHandler {
    public async handleExit(code: number, timeout = 5000): Promise<void> {
        try {
            logger.info(`Attempting graceful shutdown with code: ${code}`);
            setTimeout(() => {
                logger.info(`Forcing a shutdown with code: ${code}`);
                process.exit(code);
            }, timeout).unref();

            if (server.listening) {
                logger.info('Terminating HTTP connections');
                await httpTerminator.terminate();
                await mongoose.connection.close();
            }
            logger.info(`Exiting gracefully with code ${code}`);
            process.exit(code);
        } catch (error) {
            logger.error(error);
            logger.error(
                `Unable to shutdown gracefully... Forcing exit with code: ${code}`
            );
            process.exit(code);
        }
    }
}

export const exitHandler = new ExitHandler();

Things I've tried:

  • 对测试和服务器脚本使用相同的环境文件(相同的错误)
  • 摆弄tsconfig和jest配置文件(相同的错误)
  • 使用mode.exports=app而不是导出默认应用程序或导出常量服务器=app(相同错误)
  • 注释掉所有中间件和路由,只导出应用程序(相同的错误)

推荐答案

我认为这是循环依赖造成的.从错误堆栈中

  at Object.<anonymous> (src/index.ts:8:27)
  at Object.<anonymous> (src/library/exitHandler/exitHandler.ts:2:1
  …
  at Object.<anonymous> (src/server.ts:2:1)
  at Object.<anonymous> (src/components/users/user.test.ts:2:1)

我看到exitHandler.ts号公路下降了server.ts点,而index.ts号公路又下降了server.ts点.但在index.tsimport app from './server'形成了一个圆圈.

更具体地说,为了从server.ts创建app,它需要exitHandler,但它需要index,而index需要server.这就像没有基本情况返回的递归.与不确定的函数递归不同,依赖项解析只会给出app作为undefined.

因此,你需要打破这个循环.使用一些依赖项注入技巧来打破exitHandlerindex之间的平局就可以了.

如果你不知道怎么做,发布exitHandler.ts代码,我会跟进的.


不是import { httpTerminator, server } from '../..';个,而是试试这个:

let server, httpTerminator;

export function injectDependency(s, h) {
  server = s;
  httpTerminator = h;
}

现在是index.ts

import { injectDependency } from "…/exitHandler"

injectDependency(server, httpTerminator);

Node.js相关问答推荐

容器端口是容器内 node 应用程序的端口吗?

利用Gemini:通过Vertex AI还是通过Google/generative-ai?

Twilio-获取呼叫录音不起作用的请求

GitLab SAST中的Nodejcan未找到匹配项

NPM:一般的npm二进制依赖可以静态构建吗?

FindOne()返回所有值,而不是一个值

如何从基于JSON的HTML/SCSS模板生成PDF?

2023年如何在Node.js中使用Gmail发送邮箱?

无法截取页面截图

为什么 client.on("messageCreate") 的 TextChannel 中缺少 nsfw 属性?

如何使用填充在mongoose 上保存新数据

如何删除mongodb中嵌套数组中所有出现的数组元素

fastify:流过早关闭

在 Atlas 触发器(Node JS)中正确初始化 Firebase 管理 SDK

更新文档数组中的文档 Mongoose

Web3.js 脚本在监听 CreatedPairs 时退出

在 ExpressJS 中将变量传递给 JavaScript

为什么 JavaScript 的 parseInt(0.0000005) 打印5?

使用 MongoDB 更新嵌套数组

node.js 异步库