Private side effects
Loading "Private Side Effects"
Run locally for transcripts
There are cases when a third-party module may introduce root-level side effects that must be excluded during the test run.
Take a look at the
authorize
function below. It accepts a userId
and queries that user in a database, using a made-up @workshop/epic-sdk
package.import { queryTable } from '@workshop/epic-sdk'
export interface User {
id: string
name: string
}
export async function authorize(userId: string) {
// As a part of authorization, we need to query the database
// for the given user. For that, we are using an imaginary
// third-party SDK package that performs SQL operations for us.
const user = await queryTable<User>('users', {
where: { id: { equals: userId } },
}).catch((error) => {
throw new Error(`Failed to fetch user by id "${userId}"`, {
cause: error,
})
})
return user
}
Naturally, you would want to exclude the actual querying logic of
queryTable
from the test, gaining control over that functions implementation. In any other situation, you would use vi.spyOn()
for that task and we done with it. But there's one catch...The
@workshop/epic-sdk
package has an internal side-effect that executes once you import that package:import { initTelemetry } from './telemetry.js'
const telemetry = initTelemetry()
await telemetry.record('start', null)
export async function queryTable<T>(
tableName: string,
query: { where: any },
): Promise<T> {
await telemetry.record('queryTable', { tableName, query })
throw new Error(`Querying "${tableName}" in tests is not allowed!`)
}
You must not allow this telemetry logic to run during your the test either! It may introduce all sorts of variables to your test results, from establishing HTTP connections to simply chipping away at your test's performance. But the most important reasonβit has nothing to do with the intention you are aiming to test.
You have to mock that side effect.
The telemetry itself is not a part of that package's public API, so you cannot even access it to try mock its implementation by conventional means. Even if it was, its indirect relationship with the
queryTable
function means you would be leaking implementation details of @workshop/epic-sdk
into your tests, making them brittle.This is a good case to mock that module entirely.
- Mock the
queryTable
implementation by usingvi.fn()
; - Mock the
@workshop/epic-sdk
module by usingvi.mock()
.
Although these two mocks have different goals, their implementation may not be as different as you think! π
As always, verify your solution by running
npm test
.