npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

enmoq

v0.0.5

Published

Comprehensive mock testing infrastructure for Node.js projects with services, repositories, queues, and external APIs

Readme

enmoq

Zero-boilerplate mock infrastructure for Node.js integration tests

Overview

enmoq provides drop-in mock implementations for every common backend dependency — repository, queue, HTTP, TigerBeetle, ClickHouse — wired automatically into Jest so your test files contain nothing but test logic.

What makes enmoq different:

  • setupMocks / teardownMocks / auto-reset run automatically via lifecycle hooks — no beforeAll/afterAll in your test files
  • useMocks() gives you typed accessors to every mock instance, sharing the same singletons your services use internally
  • HTTP history is persisted to disk after every test automatically — inspect it without adding any code
  • Zero network calls, zero external processes

Installation

npm install --save-dev enmoq

Setup

1. jest.config.js

module.exports = {
  testEnvironment: 'node',

  // enmoq registers beforeAll / afterAll / beforeEach automatically
  setupFilesAfterEnv: ['enmoq/src/jest/setup.js'],

  moduleNameMapper: {
    // Route @app-core/* to enmoq mocks
    '^@app-core/repository-factory$': '<rootDir>/node_modules/enmoq/src/core/mock-repository',
    '^@app-core/queue$': '<rootDir>/node_modules/enmoq/src/core/mock-queue',
    '^@app-core/http-request$': '<rootDir>/node_modules/enmoq/src/core/mock-http',
    '^tigerbeetle-node$': '<rootDir>/node_modules/enmoq/src/core/mock-tigerbeetle',

    // Real modules (not mocked)
    '^@app-core/validator$': '<rootDir>/core/validator-vsl',
    '^@app-core/errors$': '<rootDir>/core/errors',
    '^@app-core/logger$': '<rootDir>/core/logger',

    // App aliases
    '^@app/repository/(.*)$': '<rootDir>/repository/$1',
    '^@app/messages/(.*)$': '<rootDir>/messages/$1',
  },

  testTimeout: 30000,
};

2. enmoq.config.js (project root — optional)

enmoq auto-discovers this file by walking up from process.cwd(). All fields are optional.

// enmoq.config.js
module.exports = {
  /** Where mock data files are written. Default: .mock-data */
  dataDir: '.mock-data',

  autoReset: {
    /** Clear all queue singletons before each test. Default: true */
    queue: true,

    /** Clear HTTP request history before each test. Default: true */
    http: true,

    /** Persist HTTP history to disk after each test (before clearing).
     *  Writes .mock-data/<session>/http/history.json. Default: true */
    persistHttp: true,

    // All false by default — repository data accumulates across tests in a suite
    repository: false,
    clickhouse: false,
    tigerbeetle: false,
  },
};

You can also point to a config file explicitly via the ENMOQ_CONFIG environment variable (absolute or cwd-relative path).

Writing tests

No beforeAll / afterAll / beforeEach needed. enmoq registers them for you.

const { useMocks } = require('enmoq');
const createOrder = require('../services/create-order');

const { queue, repository } = useMocks();

describe('createOrder', () => {
  it('creates the record and enqueues a job', async () => {
    const result = await createOrder({ customer_id: '...', item_id: '...', quantity: 1 });

    // Assert what landed in the repository
    const stored = await repository().findOne({ query: { _id: result.id } });
    expect(stored.status).toBe('pending');

    // Assert the queue job
    const jobs = queue('orders').getJobs(['waiting']);
    expect(jobs).toHaveLength(1);
    expect(jobs[0].data.order_id).toBe(result.id);
  });
});

useMocks()

Call once at the top of a test file (module scope). Returns accessors that share the same singleton instances your services hold internally.

const { queue, http, repository, clickhouse, tigerbeetle } = useMocks();

| Accessor | Returns | | --------------- | ------------------------------------------------------------------------------------------------------- | | queue(name) | The BullMock singleton for queue name name — same object createQueue({ queueName: name }) returns | | http | The MockHttpRequest module (static interface) | | repository() | The MockRepository session created during setup | | clickhouse() | The MockClickHouse instance | | tigerbeetle() | The MockTigerBeetle instance |

Repository mock

Drop-in replacement for @app-core/repository-factory. Backed by an in-memory JSON store scoped per test session.

const { repository } = useMocks();

// findOne, findMany, create, update, delete — same API as the real factory
const record = await repository().findOne({ query: { status: 'pending' } });

Supported query operators: $gt, $gte, $lt, $lte, $in, $nin, $or, $and, $ne
Supported update operators: $set, $inc, $push, $pull, $unset
Also supports: aggregation pipeline, sessions, transactions, soft-delete (paranoid flag)

If a Mongoose model matching the collection name exists in @app/models, enmoq reads its __appConfig (paranoid, uniqueFields, defaults) automatically.

Queue mock

Drop-in replacement for @app-core/queue. Singletons are keyed by queue name and cleared between tests (autoReset.queue: true).

const { queue } = useMocks();

// Access the same singleton your service used
const jobs = queue('notifications').getJobs(['waiting']);

// Process jobs manually during tests
await queue('notifications').processJobs();

// Register a processor to test job execution
queue('orders').process('process-order', async (job) => {
  // handler body
});

Key queue methods:

| Method | Description | | ------------------------ | ---------------------------------------------------------------------------- | | getJobs(statusArray) | Returns jobs filtered by status (waiting, active, completed, failed) | | getJobCounts() | Returns counts per status | | processJobs(limit?) | Manually triggers processing for waiting jobs | | process(name, handler) | Registers a job processor | | empty() | Clears in-memory jobs (does not write to disk) | | pause() / resume() | Pause/resume processing |

HTTP mock

Drop-in replacement for @app-core/http-request. Register per-test responses using behaviorRegistry, then assert what was called via getRequestHistory().

const { http } = useMocks();

// Register a response (same pattern key = overwrites previous)
http.mockHttp.behaviorRegistry.register(
  'GET',
  '/v1/convert', // regex matched against the full URL
  async () => ({ status: 200, body: { rate: 1.18 } }),
  'rates-convert' // name — optional, used for overwriting
);

// Assert what was sent
const history = http.mockHttp.getRequestHistory();
// history entries are flat: { method, url, headers, data, response, timestamp }

const last = http.mockHttp.getLastRequest();
expect(last.url).toContain('from=USD');

mockHttp utility methods:

| Method | Description | | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | behaviorRegistry.register(method, urlPattern, handler, name?) | Register a mock response. urlPattern is used as a regex. | | getRequestHistory() | Returns all recorded requests as { method, url, headers, data, response, timestamp }[] | | getLastRequest() | Returns the most recent history entry | | clearHistory() | Wipe request history only | | clearBehaviors() | Remove all registered behaviors (leaves history intact — use in afterEach) | | clearAll() | History + resource registry + state (not behaviors) | | reset() | Full reset — clearAll() + remove behaviors + re-initialize | | persist() | Write history to .mock-data/<session>/http/history.json |

HTTP history persistence:

With autoReset.persistHttp: true (default), enmoq's afterEach automatically calls persist() before the next beforeEach clears history. You get a live snapshot of each test's outbound calls at:

.mock-data/<session>/http/history.json   ← last test's requests
.mock-data/<session>/http/resources.json
.mock-data/<session>/http/states.json

Per-test behavior isolation:

Use clearBehaviors() in your test file's afterEach to prevent registered handlers bleeding between tests. Don't use clearAll() here — that wipes history before enmoq can persist it.

afterEach(() => {
  http.mockHttp.clearBehaviors();
});

Simulating errors:

http.mockHttp.behaviorRegistry.register(
  'POST',
  '/v1/payments',
  async () => ({ status: 503, body: { message: 'Service Unavailable' } }),
  'payments-down'
);

The mock client throws with error.response.status set — same shape as a real axios error.

Data directory

All mock data is written under dataDir (default .mock-data), scoped by session:

.mock-data/
  <session-id>/
    repository/     ← JSON files per collection
    queue/
      <name>/       ← jobs.json per queue
    http/
      history.json  ← persisted request history
      resources.json
      states.json
    clickhouse/
    tigerbeetle/

Each Jest run gets a unique session ID (session-<timestamp>) unless you set TEST_SESSION_DIR explicitly.

Environment variables

| Variable | Description | | ------------------ | --------------------------------------------------------------------------- | | ENMOQ_CONFIG | Absolute or cwd-relative path to the config file (overrides auto-discovery) | | ENMOQ_DEBUG=true | Log session ID on startup | | MOCK_DATA_DIR | Override the data directory without a config file | | TEST_SESSION_DIR | Pin the session ID (useful for multi-file test suites that share data) | | RATES_API_URL | Example — real services read their base URLs from env; point them anywhere |

Exports

const {
  // Lifecycle (called automatically by jest/setup.js)
  setupMocks,
  teardownMocks,
  resetMocks,
  getMockInstances,
  getMockDataPath,

  // DX accessor
  useMocks,

  // Raw mock classes (for advanced use)
  MockRepository,
  MockQueue,
  MockHttp,
  MockClickHouse,
  MockTigerBeetle,

  // Jest helpers
  generateJestMappings,
  autoMapCoreModules,
  jestPreset,

  // Session management
  SessionManager,
} = require('enmoq');

License

ISC