Knex Mock Client
knex-mock-client is a testing utility designed to provide a comprehensive mock client for Knex.js, enabling developers to write isolated unit tests for database interactions without needing an actual database connection. The current stable version is 3.0.2. The library maintains an active release cadence, frequently publishing patch and minor versions to address bug fixes, improve type safety, and add new features like support for transaction isolation levels. Its key differentiators include flexible query matching (string, regex, or custom function), explicit control over response data and errors for different query types (select, insert, update, delete, any), and the ability to track executed queries for assertions. It's built to integrate seamlessly into testing frameworks like Jest by allowing the Knex client to be mocked with `MockClient`.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'insert')
cause The Knex instance being used in the test is not correctly configured with `MockClient`, or the module exporting the Knex instance was not properly mocked.fixVerify that your `knex` instance is initialized as `knex({ client: MockClient })` and that any module providing this instance is mocked correctly using your testing framework's facilities (e.g., `jest.mock`). -
Query not handled by tracker: { method: 'select', sql: '...', bindings: [...] }cause The `knex-mock-client` tracker received a query that does not match any of the configured `on.response` or `on.responseOnce` handlers.fixAdd a `tracker.on.<method>('<query matcher>').response(...)` handler for the specific query, ensuring the matcher (string, regex, or function) accurately covers the executed SQL. -
Type 'typeof MockClient' is not assignable to type 'ClientConstructor<any>'
cause This usually indicates a mismatch in TypeScript versions, `knex` versions, or an incorrect `knex` import that leads to type incompatibility for the `client` property.fixEnsure you are using `knex@>=2.0.0` and `knex-mock-client@>=2.0.1`. Also, ensure `knex` is imported from `knex` directly (e.g., `import { knex } from 'knex';`).
Warnings
- breaking Version 3.0.0 of `knex-mock-client` dropped support for Node.js versions older than 18. Users on Node.js 16 or earlier must upgrade their Node.js environment or remain on `knex-mock-client` v2.x.x.
- breaking In version 2.0.0, the `MockClient.tracker` property was changed from static to an instance property. This means you can no longer access `MockClient.tracker` directly, but instead need to obtain the tracker instance via `createTracker(db)` where `db` is your Knex instance configured with `MockClient`.
- gotcha `knex-mock-client` relies on `knex` as a peer dependency, specifically requiring `knex@>=2.0.0`. Ensure you have a compatible version of `knex` installed in your project, otherwise, you may encounter type mismatches or runtime errors.
- gotcha When mocking the Knex client, especially with module mocking utilities like `jest.mock`, it's crucial to correctly configure the Knex instance to use `MockClient`. Incorrect setup can lead to the mock not being applied, resulting in real database calls or errors like 'Client not supported'.
Install
-
npm install knex-mock-client -
yarn add knex-mock-client -
pnpm add knex-mock-client
Imports
- createTracker
const { createTracker } = require('knex-mock-client');import { createTracker } from 'knex-mock-client'; - MockClient
import MockClient from 'knex-mock-client';
import { MockClient } from 'knex-mock-client'; - Tracker
import type { Tracker } from 'knex-mock-client/tracker';import { Tracker } from 'knex-mock-client';
Quickstart
import { createTracker, MockClient } from 'knex-mock-client';
import { knex } from 'knex'; // Peer dependency
// Imagine this is your actual DB setup file that gets mocked
const db = knex({ client: MockClient });
// A function that uses the knex instance
async function getUsersOlderThan(age: number) {
return db('users').where('age', '>', age).select();
}
describe('User Service', () => {
let tracker: ReturnType<typeof createTracker>;
beforeEach(() => {
tracker = createTracker(db); // Initialize a new tracker before each test
});
afterEach(() => {
tracker.reset(); // Clear all mock handlers and query history after each test
});
it('should retrieve users older than a given age', async () => {
const mockUsers = [
{ id: 1, name: 'Alice', age: 30 },
{ id: 2, name: 'Bob', age: 35 }
];
// Configure the tracker to respond to a specific query
tracker.on.select('users').response(mockUsers);
const users = await getUsersOlderThan(25);
expect(users).toEqual(mockUsers);
// Verify the query that was executed
const selectHistory = tracker.history.select;
expect(selectHistory).toHaveLength(1);
expect(selectHistory[0].method).toEqual('select');
expect(selectHistory[0].sql).toContain('select * from `users` where `age` > ?');
expect(selectHistory[0].bindings).toEqual([25]);
});
it('should handle no users found', async () => {
tracker.on.select('users').responseOnce([]);
const users = await getUsersOlderThan(40);
expect(users).toEqual([]);
expect(tracker.history.select).toHaveLength(1);
});
});