TypeError: _app.app.close is not a function using jest 27.5.1

TypeError: _app.app.close is not a function using jest 27.5.1

我有一个测试在 Jest 27.5.1 上失败(babel-jest 在同一版本)并出现此错误

我在使用 Jest 26.6.3 时没有收到此错误(babel-jest 也在 26.6.3) 我的测试文件

import supertest from 'supertest';
import {
  buildAccount,
  buildAuthenticationType,
  createAccount,
  createAuthenticationType,
} from './factories';
import {
  startDatabase,
  ENDPOINT_PREFIX,
  clearDatabase,
  doLogin,
} from './utils';
import { Account, AuthenticationType } from 'data/models';
import { app } from 'server/app';

const agent = supertest.agent(app);
const ENDPOINT = `${ENDPOINT_PREFIX}/account`;

describe('Account tests', () => {
  beforeEach(async () => {
    await startDatabase();
    await doLogin(agent);
  });

  afterAll(async () => {
    await clearDatabase();
    console.log(typeof app.close, app.close);
    await app.close();
  });

  test('/POST - Response with a new created account', async () => {
    const relAuthenticationTypeIdDict = await buildAuthenticationType({});
    const relFakeAuthenticationTypeId = await createAuthenticationType(
      relAuthenticationTypeIdDict
    );

    const fakeAccount = await buildAccount({
      authenticationTypeId: relFakeAuthenticationTypeId.authenticationTypeId,
    });

    const response = await agent.post(ENDPOINT).send(fakeAccount);

    expect(response.status).toBe(201);
    expect(response.statusCode).toBe(201);

    const responseAccount = response.body.data;

    const account = await Account.findByPk(responseAccount.accountId);

    expect(account.email).toBe(fakeAccount.email);
    expect(account.emailVerified).toBe(fakeAccount.emailVerified);
    expect(account.isPrimary).toBe(fakeAccount.isPrimary);
    expect(account.username).toBe(fakeAccount.username);
    expect(account.password).toBe(undefined);
    expect(account.pictureUrl).toBe(fakeAccount.pictureUrl);
    expect(account.socialAccountId).toBe(fakeAccount.socialAccountId);

    expect(account.authenticationTypeId).toBe(fakeAccount.authenticationTypeId);
  });
});

我的app.js文件

import express from 'express';
import path from 'path';
import logger from 'morgan';
import passport from 'passport';
import swaggerUi from 'swagger-ui-express';

import { adminbroRouter } from './routes/adminbro.route';
import { router } from './routes';
import { sessionParser } from './session';
import { swaggerDocument } from './swagger';

import {
  errorHandler,
  responseHandler,
  pageNotFoundHandler,
  initResLocalsHandler,
} from './middlewares';

import { refreshFeedItemTagsQueue } from 'jobs/refresh-feed-item-tags';
import { ACCEPTED, NOT_FOUND, OK } from 'http-status';

import './passport';

const app = express();

// Swagger
app.use(
  '/swagger',
  swaggerUi.serveFiles(swaggerDocument),
  swaggerUi.setup(swaggerDocument)
);

// Middlewares
app.use(logger('dev'));
app.use('/admin', adminbroRouter);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(sessionParser);
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
app.use(initResLocalsHandler);

app.use(process.env.BASE_PATH, router);

// Use custom response handler
app.use(responseHandler);

// Use custom error handler
app.use(errorHandler);

// Page not found
app.use(pageNotFoundHandler);

export { app };

我的server.js文件

// eslint-disable-next-line import/first
import http from 'http';
import { app } from './app';
import { sessionParser } from './session';
import { websocketServer } from './ws';
import 'jobs/repeatable';

const server = http.createServer(app);

server.on('upgrade', (request, socket, head) => {
  sessionParser(request, {}, () => {
    websocketServer.handleUpgrade(request, socket, head, (ws) => {
      websocketServer.emit('connection', ws, request);
    });
  });
});

/* istanbul ignore next */
const PORT = process.env.PORT || 8000;
/* istanbul ignore next */
server.listen(PORT, () => {
  // eslint-disable-next-line no-console
  console.log(`Express server listening on port ${PORT}`);
});

怎么可能旧版本的 jest 没有出现这个错误而新版本出现了?

appexpress() 创建,没有 close 方法。但是const server = http.createServer(app);server有一个close方法。

您可以启动服务器并监听 beforeAll 中的连接并在 afterAll 中调用 server.close。为此,您需要导出 server 以便测试文件可以获得 server.

if (require.main === module) 块中的语句只有在 运行 此脚本被 node server.js 时才会执行。

例如

app.ts

import express from 'express';

const app = express();

app.get('/heartbeat', (req, res) => {
  res.sendStatus(200);
});

export { app };

server.ts:

import http from 'http';
import { app } from './app';

const server = http.createServer(app);

const PORT = process.env.PORT || 8000;

if (require.main === module) {
  server.listen(PORT, () => {
    console.log(`Express server listening on port ${PORT}`);
  });
}

export { server };

server.test.ts:

import supertest from 'supertest';
import { app } from './app';
import { server } from './server';

const agent = supertest.agent(app);

describe('server', () => {
  before((done) => {
    server.listen(7890, () => {
      console.log('Test server listening on port: 7890');
      done();
    });
  });
  after((done) => {
    server.close(done);
  });
  it('should pass', () => {
    return agent.get('/heartbeat').expect(200);
  });
});

测试结果:

  server
Test server listening on port: 7890
    ✓ should pass


  1 passing (20ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   84.62 |       75 |      50 |   84.62 |                   
 app.ts    |     100 |      100 |     100 |     100 |                   
 server.ts |      75 |       75 |       0 |      75 | 9-10              
-----------|---------|----------|---------|---------|-------------------