docs: update to prefer new apis option and use mockApis

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-01 21:09:24 +01:00
parent 09032d7bd4
commit 68d2c57d94
3 changed files with 65 additions and 89 deletions
+14 -6
View File
@@ -2,21 +2,29 @@
'@backstage/frontend-test-utils': patch
---
Added support for API overrides in `createExtensionTester` and `renderInTestApp`. You can now pass an `apis` option to override specific APIs when testing extensions:
Added an `apis` option to `createExtensionTester` and `renderInTestApp` to override APIs when testing extensions. Use the `mockApis` helpers to create mock implementations:
```typescript
import { identityApiRef } from '@backstage/frontend-plugin-api';
import { mockApis } from '@backstage/frontend-test-utils';
// Override APIs in createExtensionTester
const tester = createExtensionTester(myExtension, {
apis: [
[errorApiRef, mockErrorApi],
[analyticsApiRef, mockAnalyticsApi],
[
identityApiRef,
mockApis.identity({ userEntityRef: 'user:default/guest' }),
],
],
});
// Override APIs in renderInTestApp
renderInTestApp(<MyComponent />, {
apis: [[errorApiRef, mockErrorApi]],
apis: [
[
identityApiRef,
mockApis.identity({ userEntityRef: 'user:default/guest' }),
],
],
});
```
The package now also exports its own implementations of `TestApiProvider`, `TestApiRegistry`, and related types, rather than re-exporting them from `@backstage/test-utils`. This consolidates common types used internally by the test utilities.
@@ -31,89 +31,50 @@ describe('Entity details component', () => {
});
```
To mock [Utility APIs](../architecture/33-utility-apis.md) that are used by your component you can use the `TestApiProvider` to override individual API implementations. In the snippet below, we wrap the component within a `TestApiProvider` in order to mock the catalog client API:
To mock [Utility APIs](../architecture/33-utility-apis.md) that are used by your component, pass API overrides to `renderInTestApp` using the `apis` option. Mock helpers are available from `@backstage/frontend-test-utils` and plugin-specific test utilities:
```tsx
import { screen } from '@testing-library/react';
import {
renderInTestApp,
TestApiProvider,
} from '@backstage/frontend-test-utils';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
import { EntityDetails } from './plugin';
describe('Entity details component', () => {
it('should render the entity name and owner', async () => {
const catalogApiMock = {
async getEntityFacets() {
return {
facets: {
'relations.ownedBy': [{ count: 1, value: 'group:default/tools' }],
},
},
}
} satisfies Partial<typeof catalogApiRef.T>;
const entityRef = stringifyEntityRef({
kind: 'Component',
namespace: 'default',
name: 'test',
});
await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApiMock]]}>
<EntityDetails entityRef={entityRef} />
</TestApiProvider>,
);
await expect(
screen.findByText('The entity "test" is owned by "tools"'),
).resolves.toBeInTheDocument();
});
});
```
This pattern also works for many other context providers. An important example is the `EntityProvider` from the `@backstage/plugin-catalog-react` package, which you can use to provide a mocked entity context to the component.
Alternatively, you can pass API overrides directly to `renderInTestApp` using the `apis` option:
```tsx
import { screen } from '@testing-library/react';
import { renderInTestApp } from '@backstage/frontend-test-utils';
import { renderInTestApp, mockApis } from '@backstage/frontend-test-utils';
import { identityApiRef } from '@backstage/frontend-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { EntityDetails } from './plugin';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
import { MyEntitiesList } from './plugin';
describe('Entity details component', () => {
it('should render the entity name and owner', async () => {
const catalogApiMock = {
async getEntityFacets() {
return {
facets: {
'relations.ownedBy': [{ count: 1, value: 'group:default/tools' }],
},
},
}
} satisfies Partial<typeof catalogApiRef.T>;
const entityRef = stringifyEntityRef({
kind: 'Component',
namespace: 'default',
name: 'test',
});
await renderInTestApp(<EntityDetails entityRef={entityRef} />, {
apis: [[catalogApiRef, catalogApiMock]],
describe('MyEntitiesList', () => {
it('should render entities owned by the current user', async () => {
await renderInTestApp(<MyEntitiesList />, {
apis: [
[
identityApiRef,
mockApis.identity({ userEntityRef: 'user:default/guest' }),
],
[
catalogApiRef,
catalogApiMock({
entities: [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: { name: 'my-component' },
spec: { type: 'service', owner: 'user:default/guest' },
},
],
}),
],
],
});
await expect(
screen.findByText('The entity "test" is owned by "tools"'),
screen.findByText('my-component'),
).resolves.toBeInTheDocument();
});
});
```
This approach provides the API overrides at the app level, which is useful when testing extensions that depend on APIs deep in the component tree.
This approach provides the API overrides at the app level, which is useful when testing components that depend on APIs deep in the component tree.
The `TestApiProvider` component is also available for standalone rendering scenarios where you're not using `renderInTestApp` or other test utilities. Context providers like `EntityProvider` from `@backstage/plugin-catalog-react` can also be used to provide a mocked entity context to the component.
## Testing extensions
@@ -141,26 +102,32 @@ describe('Index page', () => {
});
```
This pattern also allows you to wrap the extension with context providers, such as the `TestApiProvider` that was introduced [above](#testing-react-components). Alternatively, you can provide API overrides directly to `createExtensionTester`:
You can also provide API overrides directly to `createExtensionTester` using the `apis` option:
```tsx
import { screen } from '@testing-library/react';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import { analyticsApiRef } from '@backstage/frontend-plugin-api';
import {
createExtensionTester,
mockApis,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import { identityApiRef } from '@backstage/frontend-plugin-api';
import { indexPageExtension } from './plugin';
describe('Index page', () => {
it('should render and track analytics', async () => {
const analyticsApiMock = { captureEvent: jest.fn() };
it('should render with a custom identity', async () => {
await renderInTestApp(
createExtensionTester(indexPageExtension, {
apis: [[analyticsApiRef, analyticsApiMock]],
apis: [
[
identityApiRef,
mockApis.identity({ userEntityRef: 'user:default/guest' }),
],
],
}).reactElement(),
);
expect(screen.getByText('Index Page')).toBeInTheDocument();
expect(analyticsApiMock.captureEvent).toHaveBeenCalled();
});
});
```
@@ -83,15 +83,16 @@ export type TestAppOptions<TApiPairs extends any[] = any[]> = {
initialRouteEntries?: string[];
/**
* API overrides to provide to the test app.
* API overrides to provide to the test app. Use `mockApis` helpers
* from `@backstage/frontend-test-utils` to create mock implementations.
*
* @example
* ```ts
* import { identityApiRef } from '@backstage/frontend-plugin-api';
* import { mockApis } from '@backstage/frontend-test-utils';
*
* renderInTestApp(<MyComponent />, {
* apis: [
* [errorApiRef, mockErrorApi],
* [analyticsApiRef, mockAnalyticsApi],
* ]
* apis: [[identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })]],
* })
* ```
*/