Skip to main content
Version: Next

Testing with Utility APIs

The mockApis namespace from @backstage/frontend-test-utils provides test implementations of the core Utility APIs. Every member of coreApis has a matching mockApis.<name>.mock() helper. Some APIs also have functional fake instances for tests that need state or other behavior.

Create Jest mocks​

Call .mock() to create an API whose methods are plain jest.fn() functions. They have no implementation by default and return undefined. Configure the methods your test uses by passing partial implementations:

import { mockApis } from '@backstage/frontend-test-utils';

const discovery = mockApis.discovery.mock({
getBaseUrl: async pluginId => `http://example.com/api/${pluginId}`,
});

await discovery.getBaseUrl('catalog'); // 'http://example.com/api/catalog'
expect(discovery.getBaseUrl).toHaveBeenCalledWith('catalog');

All methods remain Jest mocks, including those supplied as partial implementations. You can change their behavior and make assertions using the usual Jest methods:

discovery.getBaseUrl.mockResolvedValue('http://other.example.com/catalog');
await expect(discovery.getBaseUrl('catalog')).resolves.toBe(
'http://other.example.com/catalog',
);
expect(discovery.getBaseUrl).toHaveBeenCalledTimes(2);

Each call to .mock() creates an independent instance. Use mocks when your test needs to control responses, simulate failures, or inspect calls.

Provide APIs to components and extensions​

Pass mocks directly to renderInTestApp, renderTestApp, or TestApiProvider. The helpers associate each instance with its API ref, so you do not need to provide the ref separately.

import { renderInTestApp, mockApis } from '@backstage/frontend-test-utils';

const discovery = mockApis.discovery.mock({
getBaseUrl: async () => 'http://example.com/api/catalog',
});

await renderInTestApp(<MyComponent />, { apis: [discovery] });

For extensions, use the same apis option:

import { renderTestApp, mockApis } from '@backstage/frontend-test-utils';

renderTestApp({
extensions: [myExtension],
apis: [mockApis.permission()],
});

For standalone component rendering, wrap the component in TestApiProvider:

import { render } from '@testing-library/react';
import { TestApiProvider, mockApis } from '@backstage/frontend-test-utils';

render(
<TestApiProvider apis={[mockApis.identity({ displayName: 'Test User' })]}>
<MyComponent />
</TestApiProvider>,
);

You can also provide any API implementation as an [apiRef, implementation] tuple. Mocks, fakes, and tuples can be mixed in the same apis array:

await renderInTestApp(<MyComponent />, {
apis: [discovery, [myCustomApiRef, myCustomApiInstance]],
});

Test app defaults​

renderInTestApp and renderTestApp provide in-memory fakes for alerts, errors, OAuth requests, storage, and toast notifications. For example, storage stays local to each test app, and posting a toast records a message without displaying a notification. Explicit entries in apis replace these defaults. The test app configures its error fake with collect: true to record errors without displaying alerts or installing global error handlers.

Feature-provided API extensions can also override these defaults. APIs that depend on installed extensions and app wiring use the test app's implementations, so routes, theme providers, translations, and plugin customizations remain available.

Use functional fake instances​

Call an API helper directly, without .mock(), to create a functional fake. Fakes implement useful behavior, such as collecting notifications or updating in-memory state. Use them to provide inputs or inspect effects of the component you are testing. Their methods are ordinary functions, so Jest call assertions do not apply.

For example, test that your AppHeader displays the configured application title. Supply a configuration fake and assert on the component's rendered output:

import { screen } from '@testing-library/react';
import { mockApis, renderInTestApp } from '@backstage/frontend-test-utils';
import { AppHeader } from './AppHeader';

it('displays the configured application title', async () => {
const config = mockApis.config({ data: { app: { title: 'Test App' } } });
await renderInTestApp(<AppHeader />, { apis: [config] });

expect(
await screen.findByRole('heading', { name: 'Test App' }),
).toBeInTheDocument();
});

Available fake instances​

HelperBehavior
mockApis.alert()Legacy Alert API fake; use mockApis.toast() for new tests. Provides getAlerts(), clearAlerts(), and waitForAlert()
mockApis.analytics()Collects analytics events; provides getEvents()
mockApis.appLanguage(options?)Selects from availableLanguages, starting with defaultLanguage or English. Updates subscribers when the language changes; passing no language resets the selection
mockApis.appLifecycle(options?)Starts finalized unless finalized: false is supplied. setFinalized() changes state and notifies subscribers
mockApis.appTheme(options?)Exposes the supplied themes and optional activeThemeId. Theme selection updates subscribers without browser storage
mockApis.config({ data })Reads configuration from a plain JSON object
mockApis.discovery({ baseUrl })Returns ${baseUrl}/api/${pluginId}, with http://example.com as the default base URL
mockApis.error(options?)Throws on unexpected errors by default. With collect: true, collects errors through getErrors() and waitForError()
mockApis.featureFlags(options?)Maintains flag state in memory; provides getState(), setState(), and clearState()
mockApis.fetch(options?)Wraps native fetch; supports identity injection and the plugin protocol
mockApis.icons({ icons })Looks up the supplied icon elements by key; defaults to an empty collection
mockApis.identity(options?)Provides configurable user identity, ownership, credentials, and profile information
mockApis.oauthRequest()Coordinates pending OAuth requests and merges requested scopes. Subscribers to authRequest$() trigger or reject requests
mockApis.permission(options?)Defaults to ALLOW; accepts a fixed authorization result or a handler
mockApis.pluginHeaderActions({ actions })Looks up arrays of header actions by plugin ID; defaults to an empty collection
mockApis.storage({ data })Maintains storage in memory, including separate buckets
mockApis.toast()Collects active toast messages through getToasts(). Handles dismiss individual messages; timeouts are not scheduled
mockApis.translation()Returns default messages from translation refs

The permission helper is for the permission plugin's API, which is defined outside coreApis.

Plugin-specific test helpers​

Plugins can provide their own helpers. For example, @backstage/plugin-catalog-react provides catalogApiMock in its /testUtils entry point:

import { renderTestApp } from '@backstage/frontend-test-utils';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';

renderTestApp({
extensions: [myExtension],
apis: [catalogApiMock({ entities: [entity] })],
});

Create your own helpers​

Use createApiMock to associate an API ref with a factory of plain Jest stubs. Use attachMockApiFactory to associate the same ref with a functional fake:

import {
attachMockApiFactory,
createApiMock,
} from '@backstage/frontend-test-utils';
import { myApiRef } from '@internal/plugin-example-react';

export function myApiMock(options?: { greeting?: string }) {
return attachMockApiFactory(myApiRef, {
greet: async () => options?.greeting ?? 'Hello!',
});
}

export namespace myApiMock {
export const mock = createApiMock(myApiRef, () => ({ greet: jest.fn() }));
}

Both variants can then be passed directly to the test utilities' apis option.