使用 MongoDB 的 Express API 开玩笑测试 - 在本地机器上运行,在 CircleCI 上失败(超时)

Jest test of Express API using MongoDB - runs on local machine, fails on CircleCI (Exceeded timeout)

正在使用 Jest 测试 RESTful API(用 Express in TypeScript 编写)。测试在我的本地机器上成功运行 (windows),但似乎在 CircleCI 上超时。

.circleci/config.ylm

version: 2.1
jobs:
  build:
    docker:
      - image: circleci/node:16.3.0
    working_directory: ~/Mealplanr-api/api
    steps:
      - checkout:
          path: ~/Mealplanr-api
      - run:
          name: Install dependencies
          command: |
            yarn install
      - run:
          name: Run tests
          command: |
            yarn test:ci
      - store_test_results:
          path: test-results
      - store_artifacts:
          path: test-results

package.json

...

"scripts": {
    "start": "nodemon --config nodemon.json src/server.ts",
    "test": "jest --watchAll",
    "test:ci": "jest"
  },

...

jest.config.js

module.exports = {
    roots: ['<rootDir>/src'],
    testMatch: [
        '**/__tests__/**/*.+(ts|tsx|js)',
        '**/?(*.)+(spec|test).+(ts|tsx|js)',
    ],
    transform: {
        '^.+\.(ts|tsx)$': 'ts-jest',
    },
};

测试如下

users.spec.ts

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

import { connectDB, closeDB } from '../connect';

describe('POST /users', () => {
    beforeAll(async () => {
        await connectDB();
    });

    afterAll(async () => {
        await closeDB();
    });

    it('Should create a new user', async () => {
        const res = await request(app).post('/users').send({
            email: 'test@test.test',
            password: '123456',
            passwordconfirmation: '123456',
        });

        const body = res.body;
        expect(body?.hasOwnProperty('_id')).toBe(true);
        expect(body?.hasOwnProperty('email')).toBe(true);
        expect(body?.hasOwnProperty('createdAt')).toBe(true);
        expect(body?.hasOwnProperty('updatedAt')).toBe(true);
    });
});

正在使用的连接函数在这里描述

connect.ts

import { connect, disconnect } from 'mongoose';
import log from './logger';
const mongoose = require('mongoose');
import { Mockgoose } from 'mockgoose';
const mockgoose = new Mockgoose(mongoose);

const dbUri = process.env.DB_URI as string;

export async function connectDB() {
    if ((process.env.NODE_ENV as string) === 'test') {
        // In test environment, we don't want to connect to the real DB.
        await mockgoose.prepareStorage();
        await connect(dbUri, {
            useNewUrlParser: true,
            useCreateIndex: true,
            useUnifiedTopology: true,
        }).catch((error) => {
            log.error('Error in connecting', error);
        });
        log.info('Mock connection success');
    } else {
        // If not in test environment, connect to the database
        await connect(dbUri, {
            useNewUrlParser: true,
            useCreateIndex: true,
            useUnifiedTopology: true,
        }).catch((error) => {
            log.error('Error in connecting', error);
        });
        log.info('Connection success');
    }
}

export async function closeDB() {
    await mockgoose.shutdown();
    await disconnect();
}

CircleCI 的输出:

#!/bin/bash -eo pipefail
yarn test:ci

yarn run v1.22.5
$ jest
Completed: 100 % (80.8mb / 80.8mbb FAIL  src/routes/users.spec.ts (30.653 s)
  ● POST /users › Should create a new user

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

       5 |
       6 | describe('POST /users', () => {
    >  7 |  beforeAll(async () => {
         |  ^
       8 |      await connectDB();
       9 |  });
      10 |

      at src/routes/users.spec.ts:7:2
      at Object.<anonymous> (src/routes/users.spec.ts:6:1)
      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (node_modules/@jest/core/build/runJest.js:387:19)
      at _run10000 (node_modules/@jest/core/build/cli/index.js:408:7)
      at runCLI (node_modules/@jest/core/build/cli/index.js:261:3)


  ● Test suite failed to run

    TypeError: Cannot read property 'on' of undefined

      40 |
      41 | export async function closeDB() {
    > 42 |  await mockgoose.shutdown();
         |                  ^
      43 |  await disconnect();
      44 | }
      45 |

      at node_modules/mockgoose/built/mockgoose.js:55:54
      at Mockgoose.Object.<anonymous>.Mockgoose.shutdown (node_modules/mockgoose/built/mockgoose.js:50:16)
      at src/connect.ts:42:18
      at step (src/connect.ts:33:23)
      at Object.next (src/connect.ts:14:53)
      at src/connect.ts:8:71
      at Object.<anonymous>.__awaiter (src/connect.ts:4:12)
      at closeDB (src/connect.ts:99:12)
      at src/routes/users.spec.ts:12:16
      at step (src/routes/users.spec.ts:33:23)
      at Object.next (src/routes/users.spec.ts:14:53)
      at src/routes/users.spec.ts:8:71
      at Object.<anonymous>.__awaiter (src/routes/users.spec.ts:4:12)
      at src/routes/users.spec.ts:11:11

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        30.842 s
Ran all test suites.
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.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Exited with code exit status 1

问题似乎是,mockgoose 已被弃用,这导致它在 CircleCI 上 运行 时无法正常工作。一个解决方案是使用 mongodb-memory-server.

Warning: This package has been deprecated! Consider using mongodb-memory-server instead.

这部分解决了问题。我目前使用的唯一版本是在 package.json:

中使用以下内容
"config": {
    "mongodbMemoryServer": {
      "version": "4.2.3"
    }
  }