1

Problem

Starting November 10th, my Jest integration tests began failing randomly with MongoDB connection errors. The tests use @testcontainers/mongodb v11.5.1 and mongodb v8.0.12.

Error:

MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
    at Timeout._onTimeout (node_modules/mongodb/src/sdam/topology.ts:591:30)

The failures are intermittent - sometimes all tests pass, sometimes a fair amount (20-50) fail with this connection error. If I run a single test file, the error might pop up there too.

● Product Import Integration Tests › Product Updates › Variable Products › Variation Creation › should create variations with correct SKUs

    MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:20017

      at Timeout._onTimeout (node_modules/mongodb/src/sdam/topology.ts:591:30)

  ● Product Import Integration Tests › Product Updates › Variable Products › Variation Creation › should create variations with correct SKUs

    MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:20017

      at Timeout._onTimeout (node_modules/mongodb/src/sdam/topology.ts:591:30)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 52 passed, 53 total
Snapshots:   0 total
Time:        55.721 s, estimated 65 s

Setup

Jest Configuration:

const config: Config = {
    globalSetup: "<rootDir>/tests/globalSetup.ts",
    globalTeardown: "<rootDir>/tests/globalTeardown.ts",
    projects: [
        {
            displayName: "integration",
            testEnvironment: "node",
            testMatch: ["<rootDir>/tests/integration/**/*.integration.test.ts"],
            setupFilesAfterEnv: ["<rootDir>/tests/integration/integrationSetup.ts"],
            testTimeout: 15000,
        },
    ],
};

globalSetup.ts (starts one shared MongoDB container):

import { MongoDBContainer } from "@testcontainers/mongodb";
import type { StartedMongoDBContainer } from "@testcontainers/mongodb";

declare global {
  var __MONGO_CONTAINER__: StartedMongoDBContainer | undefined;
}

export default async function globalSetup() {
  const mongoContainer = await new MongoDBContainer("mongo:8.0.12").start();
  global.__MONGO_CONTAINER__ = mongoContainer;
  process.env.MONGO_CONTAINER_URI = mongoContainer.getConnectionString();
}

integrationSetup.ts (runs per worker, creates isolated databases):

beforeAll(async () => {
  const baseMongoUri = process.env.MONGO_CONTAINER_URI;
  if (!baseMongoUri) {
    throw new Error("MongoDB container URI not found");
  }
  
  // Each worker gets its own database
  const workerId = process.env.JEST_WORKER_ID || "1";
  const dbName = `test-db-${workerId}`;
  const mongoUri = `${baseMongoUri}/${dbName}`;
  process.env.MONGODB_URI = mongoUri;
  
  await mongoose.connect(mongoUri, { directConnection: true });
}, 60000);

afterAll(async () => {
  if (mongoose.connection.readyState === 1) {
    await mongoose.connection.db.dropDatabase();
    await mongoose.disconnect();
  }
});

afterEach(async () => {
  if (mongoose.connection.readyState === 1 && mongoose.connection.db) {
    const collections = await mongoose.connection.db.collections();
    for (const collection of collections) {
      await collection.deleteMany({});
    }
  }
});

Env Configuration env.ts

import zennv from "zennv";
import dotenv from "dotenv";
import z from "zod";

// Load appropriate .env file based on NODE_ENV
// zennv uses dotenv under the hood, so we can load it here
// to ensure the environment variables are available before zennv processes them.
// https://github.com/tomanagle/zennv

// Only load dotenv if it hasn't been loaded already
if (!process.env.DOTENV_LOADED) {
  if (process.env.NODE_ENV === "test") {
    dotenv.config({ path: ".env.test", override: false });
  } else {
    dotenv.config({ override: false });
  }
  process.env.DOTENV_LOADED = "true";
}

export const env = zennv({
  dotenv: false, // We've already loaded dotenv
  schema: z.object({
    MONGODB_URI: z.string().default("mongodb://localhost:27017"),
  }),
});

What I've Tried

I changed the MongoDB port in .env.test from 27017 to 20017 to verify which configuration was being used. The error then showed ECONNREFUSED 127.0.0.1:20017, confirming that in some cases, tests are using the .env.test value instead of the dynamically set MONGODB_URI from the testcontainer.

This suggests that either:

  1. Some tests start before globalSetup completes, or
  2. Some workers don't receive the updated process.env.MONGODB_URI

Question

How can I ensure that all Jest workers reliably receive the MongoDB URI from integrationSetup before any tests execute?

Is there a race condition in my setup where parallel workers might start running tests before the environment variable is propagated? Should I be using a different mechanism to share the db connection string across workers?

Environment

  • Node.js v22.18.0
  • Jest v30.1.2
0

1 Answer 1

-1

The problem was an incorrect mock.

In integration.setup.ts, I had this line of code:

jest.mock("../../src/plugins/agenda.plugin");

Which was to mock this out:

import Agenda from "agenda";
import { env } from "../config/env";

export const agenda = new Agenda({
  db: {
    address: env.MONGODB_URI,
    collection: `subscriptionJobs_${env.SERVER_INSTANCE}`,
  },
});

This mock was incorrect. I changed it to this:

const mockAgenda = {
  start: jest.fn().mockResolvedValue(undefined),
  stop: jest.fn().mockResolvedValue(undefined),
  define: jest.fn(),
  schedule: jest.fn().mockResolvedValue(undefined),
  every: jest.fn().mockResolvedValue(undefined),
  now: jest.fn().mockResolvedValue(undefined),
  cancel: jest.fn().mockResolvedValue(undefined),
  close: jest.fn().mockResolvedValue(undefined),
  on: jest.fn(),
};

jest.mock("../../src/plugins/agenda.plugin", () => ({
  agenda: mockAgenda,
}));

And all tests passed. Agenda was not properly mocked before, and would attempt to connect to the database using the environment variable from .env.test.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.