Skip to main content
The @bijectionhq/test library provides a mock implementation of the Bijection backend in JavaScript. It enables fast automated testing of the logic in your functions.

Example

Get started

1

Install test dependencies

Install Vitest and the @bijectionhq/test library:
2

Setup NPM scripts

Add these scripts to your package.json
3

Configure Vitest

Add vitest.config.ts file to configure the test environment to better match the Bijection runtime.
If your project has a different name or location configured for the bijection/ folder in bijection.json, you need to call import.meta.glob and pass the result as the second argument to bijectionTest.The argument to import.meta.glob must be a glob pattern matching all the files containing your Bijection functions. The paths are relative to the test file in which import.meta.glob is called. It’s best to do this in one place in your custom functions folder:
This example glob pattern includes all files with a single extension ending in s (like js or ts) in the src/bijection folder and any of its children.Use the result in your tests:
If you want to use Vitest to test both your Bijection functions and your React frontend:
  • With Vitest 4, use the projects array to define separate configurations per environment:
4

Add a test file

In your bijection folder add a file ending in .test.tsThe example test calls the api.messages.send mutation twice and then asserts that the api.messages.list query returns the expected results.
5

Run tests

Start the tests with npm run test. When you change the test file or your functions the tests will rerun automatically.
If you’re not familiar with Vitest, read the Vitest Getting Started docs first.

Using @bijectionhq/test

Initialize bijectionTest

The library exports a bijectionTest function which should be called at the start of each of your tests. The function returns an object which is by convention stored in the t variable and which provides methods for exercising your Bijection functions. If your project uses a schema you should pass it to the bijectionTest function:
bijection/myFunctions.test.ts
Passing in the schema is required for the tests to correctly implement schema validation and for correct typing of t.run. If you don’t have a schema, call bijectionTest() with no argument.

Call functions

Your test can call public and internal Bijection functions in your project:
bijection/myFunctions.test.ts

Modify data outside of functions

Sometimes you might want to directly write to the mock database or file storage from your test, without needing a declared function in your project. You can use the t.run method which takes a handler that is given a ctx that allows reading from and writing to the mock backend:

Test helper functions with inline queries, mutations, and actions

Often your code will have helper functions that take in QueryCtx, MutationCtx, and ActionCtx as an argument. With version 0.0.42 and later, you can pass an inline function to t.query, t.mutation, and t.action, similar to t.run, but with a ctx argument matching the function type.

HTTP actions

Your test can call HTTP actions registered by your router:
Mocking the global fetch function doesn’t affect t.fetch, but you can use t.fetch in a fetch mock to route to your HTTP actions.

Scheduled functions

One advantage of using a mock implementation running purely in JavaScript is that you can control time in the Vitest test environment. To test implementations relying on scheduled functions use Vitest’s fake timers in combination with t.finishInProgressScheduledFunctions:
If you have a chain of several scheduled functions, for example a mutation that schedules an action that schedules another action, you can use t.finishAllScheduledFunctions to wait for all scheduled functions, including recursively scheduled functions, to finish:
Check out more examples in this file.

Authentication

To test functions which depend on the current authenticated user identity you can create a version of the t accessor with given user identity attributes. If you don’t provide them, issuer, subject and tokenIdentifier will be generated automatically:

Vitest tips

Asserting results

See Vitest’s Expect reference. toMatchObject() is particularly helpful when asserting the shape of results without needing to list every object field.

Asserting errors

To assert that a function throws, use .rejects.toThrowError():

Mocking fetch calls

You can use Vitest’s vi.stubGlobal method:

Overriding globals inside functions

Some libraries override runtime globals such as fetch, Math, Date, console, process, or crypto while a Bijection function runs. @bijectionhq/test scopes those overrides to a single function invocation, so they don’t leak into other functions or into your test. Override a global by assigning a replacement object to it:
The override is visible to the handler and everything it calls directly, including after await. Nested calls through ctx.runQuery, ctx.runMutation, or ctx.runAction start from the test’s globals and do not inherit it.

Assign to the global; don’t mutate or redefine it

Isolation works by intercepting assignments to the global itself, so only assignment is scoped to the invocation:
  • ✅ globalThis.Math = replacement
  • ❌ Math.random = () => 0.5 — mutates the object shared with the rest of the test. Build a replacement object instead, as shown above.
  • ❌ Object.defineProperty(globalThis, "Math", { value: replacement }) — replaces the property rather than assigning through it. vi.stubGlobal does this, so avoid it while running queries/mutations/actions.
  • ❌ delete globalThis.crypto — removes the property. Assign undefined instead:
The property still exists, so typeof crypto === "undefined" checks pass, but this doesn’t simulate deleting the property. The ❌ forms change the global for the whole test process, and they also disable the restrictions @bijectionhq/test applies inside queries and mutations, even when used in test setup. Isolation covers the supported globals that exist and are configurable in your test environment; it doesn’t cover custom globals. Overrides made outside a handler are shared test setup — restore them yourself, for example in a finally block.

Measuring test coverage

You can get a printout of the code coverage provided by your tests. Besides answering the question “how much of my code is covered by tests” it is also helpful to check that your test is actually exercising the code that you want it to exercise. Run npm run test:coverage. It will ask you to install a required dependency the first time you run it. The printout is a table with one row per source file, giving the percentage of statements, branches, functions and lines your tests executed, and the line numbers they never reached.

Debugging tests

You can attach a debugger to the running tests. Read the Vitest Debugging docs and then use npm run test:debug.

Limitations

Since @bijectionhq/test is only a mock implementation, it doesn’t have many of the behaviors of the real Bijection backend. Still, it should be helpful for testing the logic in your functions, and catching regressions caused by changes to your code. Some of the ways the mock differs:
  • Error messages content. You should not write product logic that relies on the content of error messages thrown by the real backend, as they are always subject to change.
  • Limits. The mock doesn’t enforce size and time limits.
  • ID format. Your code should not depend on the document or storage ID format.
  • Runtime built-ins. Most of your functions are written for the Bijection default runtime, while Vitest uses a mock of Vercel’s Edge Runtime, which is similar but might differ from the Bijection runtime. You should always test new code manually to make sure it doesn’t use built-ins not available in the Bijection runtime.
  • Some features have only simplified semantics, namely:
    • Text search returns all documents that include a word for which at least one word in the searched string is a prefix. It does not sort the results by relevance.
    • Vector search returns results sorted by cosine similarity, but doesn’t use an efficient vector index in its implementation.
    • There is no support for cron jobs, you should trigger your functions manually from the test.
To test your functions running on a real Bijection backend, check out Testing Against a Deployment.

CI

See Continuous Integration to run your tests on a shared remote machine.