Slonik added a new feature โ connection mocking.
Mocking allows you to test parts of the code that interact with the database without the database. Slonik is a PostgreSQL database client for Node.js and it provides two methods to mock queries against the database.
Use createMockPool to create a mock connection.
Use createMockQueryResult to create a mock query result.
Their API is as follows:
import {
createMockPool,
createMockQueryResult,
} from 'slonik';type OverridesType = {|
+query: (
sql: string,
values: $ReadOnlyArray<PrimitiveValueExpressionType>,
) => Promise<QueryResultType<QueryResultRowType>>,
|};createMockPool(
overrides: OverridesType
): DatabasePoolType;createMockQueryResult(
rows: $ReadOnlyArray<QueryResultRowType>
): QueryResultType<QueryResultRowType>;
If you are already using Slonik in your codebase, then in order to test the individual methods, switch createPool with createMockPool in the test setup and provide a stub/ spy to the createMockPool constructor. All the executed queries will now call query method and you can choose an arbitrary result for 1st, 2nd, etc. invocations.
Here is an example updatePassword implementation and tests:
import sinon from 'sinon';
import {
createMockPool,
createMockQueryResult,
sql,
} from 'slonik';
import bcrypt from 'bcrypt';const query = sinon
.stub()
.onCall(0)
.returns(
createMockQueryResult([])
)
.onCall(1)
.returns(
createMockQueryResult([
{
id: 'bar',
},
])
)
.onCall(2)
.throws();
const updatePassword = async (connection, userId, newPassowrd) => {
const lastPasswordHash = await connection.maybeOneFirst(sql`
SELECT password_hash
FROM user_account_password
WHERE
user_id = ${userId}
ORDER BY created_at DESC
LIMIT 1
`);
if (lastPasswordHash) {
if (bcrypt.compare(newPassowrd, lastPasswordHash)) {
throw new Error('New password must be different from the last password.');
}
}
await connection.query(sql`
INSERT INTO user_account_password (user_id, password_hash)
VALUES (${userId}, ${bcrypt.hashSync(newPassowrd, 10)})
`);
await connection.query(sql`
UPDATE user_account
SET password = ${bcrypt.hashSync(newPassowrd, 10)}
WHERE id = ${userId}
`);
};
newPassowrd(createMockPool(), 1, 'foo');
// Write assertions about `query` method.
// @see https://sinonjs.org/releases/latest/spies/
This test provides mock results to the first and second queries ([] set and [{ id: 'bar' }]) and guards that if there are more query invocations, then an error must be raised.If you need to assert the query SQL & the bound values in addition to pre-defining their results, then you can use calledWith and even calledBefore to provide fake results and to assert their order of invocation. Refer to Slonik and sinon documentation for more information.
These methods for mocking a database connection allow you to write unit tests for code that depend on the database state without running a database.
1 like ยท 1.1K views ยท Mar 16, 2021
A step by step guide to mocking Postgres queries in Node.js using Slonik