Testing Jazz Apps

As you develop your Jazz app, you might find yourself needing to test functionality relating to sync, identities, and offline behaviour. The jazz-tools/testing utilities provide helpers to enable you to do so.

Core test helpers

Jazz provides some key helpers that you can use to simplify writing complex tests for your app's functionality.

setupJazzTestSync

This should normally be the first thing you call in your test setup, for example in a beforeEach or beforeAll block. This function sets up an in-memory sync node for the test session, which is needed in case you want to test data synchronisation functionality. Test data is not persisted, and no clean-up is needed between test runs.

import { co, z } from "jazz-tools";
import { beforeEach, describe, expect, test } from "vitest";
import {
  createJazzTestAccount,
  runWithoutActiveAccount,
  setActiveAccount,
  setupJazzTestSync,
} from "jazz-tools/testing";
const MyAccountSchema = co.account({
  profile: co.profile(),
  root: co.map({}),
});

describe("My app's tests", () => {
  beforeEach(async () => {
    await setupJazzTestSync();
  });

  test("I can create a test account", async () => {
    // See below for details on createJazzTestAccount()
    const account1 = await createJazzTestAccount({
      AccountSchema: MyAccountSchema,
      isCurrentActiveAccount: true,
    });
    expect(account1).not.toBeUndefined();
    // ...
  });
});

createJazzTestAccount

After you've created the initial account using setupJazzTestSync, you'll typically want to create user accounts for running your tests.

You can use createJazzTestAccount() to create an account and link it to the sync node. By default, this account will become the currently active account (effectively the 'logged in' account).

You can use it like this:

const account = await createJazzTestAccount({
  AccountSchema: MyAccountSchema,
  isCurrentActiveAccount: true,
  creationProps: {},
});

AccountSchema

This option allows you to provide a custom account schema to the utility to be used when creating the account. The account will be created based on the schema, and all attached migrations will run.

isCurrentActiveAccount

This option (disabled by default) allows you to quickly switch to the newly created account when it is created.

const account1 = await createJazzTestAccount({
  isCurrentActiveAccount: true,
});

const group1 = co.group().create(); // Group is owned by account1;

const account2 = await createJazzTestAccount();
const group2 = co.group().create(); // Group is still owned by account1;

creationProps

This option allows you to specify creationProps for the account which are used during the account creation (and passed to the migration function on creation).

Managing active Accounts

During your tests, you may need to manage the currently active account after account creation, or you may want to simulate behaviour where there is no currently active account.

setActiveAccount

Use setActiveAccount() to switch between active accounts during a test run.

You can use this to test your app with multiple accounts.

const account1 = await createJazzTestAccount({
  isCurrentActiveAccount: true,
});
const account2 = await createJazzTestAccount();
const group1 = co.group().create(); // Group is owned by account1;
group1.addMember(account2, "reader");

const myMap = MyMap.create(
  {
    text: "Created by account1",
  },
  { owner: group1 },
);
const myMapId = myMap.$jazz.id;

setActiveAccount(account2);
// myMap is still loaded as account1, so we need to load again as account2
const myMapFromAccount2 = await MyMap.load(myMapId);

if (myMapFromAccount2.$isLoaded) {
  expect(myMapFromAccount2.text).toBe("Created by account1");
  expect(() =>
    myMapFromAccount2.$jazz.set("text", "Updated by account2"),
  ).toThrow();
}

runWithoutActiveAccount

If you need to test how a particular piece of code behaves when run without an active account.

const account1 = await createJazzTestAccount({
  isCurrentActiveAccount: true,
});

runWithoutActiveAccount(() => {
  expect(() => co.group().create()).toThrow(); // can't create new group
});

Managing Context

To test UI components, you may need to create a mock Jazz context.

In most cases, you'd use this for initialising a provider. You can see how we initialise a test provider for React tests here, or see how you could integrate with @testing-library/react here.

Simulating connection state changes

You can use MockConnectionStatus.setIsConnected(isConnected: boolean) to simulate disconnected and connected states (depending on whether isConnected is set to true or false).

Next Steps

You're ready to start writing your own tests for your Jazz apps now. For further details and reference, you can check how we do our testing below.