From 68d2c57d944e7de1055080969a3429de1331c643 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 1 Feb 2026 21:09:24 +0100 Subject: [PATCH] docs: update to prefer new apis option and use mockApis Signed-off-by: Patrik Oldsberg --- .changeset/api-override-test-utils.md | 20 ++- .../building-plugins/02-testing.md | 123 +++++++----------- .../src/app/renderInTestApp.tsx | 11 +- 3 files changed, 65 insertions(+), 89 deletions(-) diff --git a/.changeset/api-override-test-utils.md b/.changeset/api-override-test-utils.md index a250dc7d15..f2c73db7f3 100644 --- a/.changeset/api-override-test-utils.md +++ b/.changeset/api-override-test-utils.md @@ -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(, { - 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. diff --git a/docs/frontend-system/building-plugins/02-testing.md b/docs/frontend-system/building-plugins/02-testing.md index adb0e0a45e..550434f373 100644 --- a/docs/frontend-system/building-plugins/02-testing.md +++ b/docs/frontend-system/building-plugins/02-testing.md @@ -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; - - const entityRef = stringifyEntityRef({ - kind: 'Component', - namespace: 'default', - name: 'test', - }); - - await renderInTestApp( - - - , - ); - - 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; - - const entityRef = stringifyEntityRef({ - kind: 'Component', - namespace: 'default', - name: 'test', - }); - - await renderInTestApp(, { - apis: [[catalogApiRef, catalogApiMock]], +describe('MyEntitiesList', () => { + it('should render entities owned by the current user', async () => { + await renderInTestApp(, { + 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(); }); }); ``` diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index 8aa863512b..93094fb7e3 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -83,15 +83,16 @@ export type TestAppOptions = { 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(, { - * apis: [ - * [errorApiRef, mockErrorApi], - * [analyticsApiRef, mockAnalyticsApi], - * ] + * apis: [[identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })]], * }) * ``` */