docs/frontend-system: add utility API testing docs
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -31,7 +31,7 @@ describe('Entity details component', () => {
|
||||
});
|
||||
```
|
||||
|
||||
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:
|
||||
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. For a deeper look at the available mock APIs and how to create your own, see [Testing with Utility APIs](../utility-apis/05-testing.md).
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
@@ -35,6 +35,14 @@ Most utility APIs are usable directly without any configuration. But they are pr
|
||||
|
||||
These cases are all described in [the main article](./04-configuring.md).
|
||||
|
||||
## Testing with utility APIs
|
||||
|
||||
> For details, [see the main article](./05-testing.md).
|
||||
|
||||
When testing frontend components and extensions, you often need to provide mock implementations of the utility APIs they depend on. The `@backstage/frontend-test-utils` package provides the `mockApis` namespace with ready-made mocks for all core utility APIs, which can be passed to test utilities like `renderInTestApp` and `TestApiProvider`.
|
||||
|
||||
These are described in detail in [the main article](./05-testing.md).
|
||||
|
||||
## Migrating from the old frontend system
|
||||
|
||||
If you want to learn how to migrate your own utility APIs from the old frontend system to the new one, that's described in the [Migrating APIs guide](../building-plugins/05-migrating.md#migrating-apis).
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: testing
|
||||
title: Testing with Utility APIs
|
||||
sidebar_label: Testing
|
||||
description: Mocking and testing Utility APIs
|
||||
---
|
||||
|
||||
When testing frontend components and extensions, you often need to provide mock implementations of the utility APIs they depend on. The `@backstage/frontend-test-utils` package provides the `mockApis` namespace with ready-made mocks for all core utility APIs.
|
||||
|
||||
## The `mockApis` namespace
|
||||
|
||||
The `mockApis` namespace is the main entry point for creating mock utility API instances in tests. It provides three usage patterns for each API:
|
||||
|
||||
### Fake instances
|
||||
|
||||
Call the API function directly to create a fake instance with simplified but functional behavior. These are useful when your test needs the API to actually work, not just be stubbed.
|
||||
|
||||
```ts
|
||||
import { mockApis } from '@backstage/frontend-test-utils';
|
||||
|
||||
const configApi = mockApis.config({ data: { app: { title: 'Test' } } });
|
||||
configApi.getString('app.title'); // 'Test'
|
||||
|
||||
const alertApi = mockApis.alert();
|
||||
alertApi.post({ message: 'hello' });
|
||||
expect(alertApi.getAlerts()).toHaveLength(1);
|
||||
```
|
||||
|
||||
### Jest mocks
|
||||
|
||||
Call `.mock()` to get an instance where every method is a `jest.fn()`. You can optionally provide partial implementations. This is useful when you want to assert that specific methods were called.
|
||||
|
||||
```ts
|
||||
import { mockApis } from '@backstage/frontend-test-utils';
|
||||
|
||||
const catalogApi = mockApis.permission.mock({
|
||||
authorize: async () => ({ result: AuthorizeResult.ALLOW }),
|
||||
});
|
||||
|
||||
// ... exercise the component ...
|
||||
|
||||
expect(catalogApi.authorize).toHaveBeenCalledTimes(1);
|
||||
```
|
||||
|
||||
### API factories
|
||||
|
||||
Call `.factory()` to get an `ApiFactory` suitable for more advanced wiring scenarios.
|
||||
|
||||
## Providing mock APIs in tests
|
||||
|
||||
### With `renderInTestApp`
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderInTestApp, mockApis } from '@backstage/frontend-test-utils';
|
||||
|
||||
await renderInTestApp(<MyComponent />, {
|
||||
apis: [
|
||||
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
||||
mockApis.config({ data: { app: { title: 'Test App' } } }),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
You can also use the `[apiRef, implementation]` tuple syntax to provide any API implementation, including ones that aren't from `mockApis`:
|
||||
|
||||
```tsx
|
||||
import { myCustomApiRef } from '../apis';
|
||||
|
||||
const myCustomApiInstance = {
|
||||
// ...
|
||||
};
|
||||
|
||||
await renderInTestApp(<MyComponent />, {
|
||||
apis: [
|
||||
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
||||
[myCustomApiRef, myCustomApiInstance],
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### With `renderTestApp`
|
||||
|
||||
The same `apis` option is available on `renderTestApp`, which is commonly used when testing extensions or entity pages:
|
||||
|
||||
```tsx
|
||||
import { renderTestApp, mockApis } from '@backstage/frontend-test-utils';
|
||||
import { createTestEntityPage } from '@backstage/plugin-catalog-react/testUtils';
|
||||
|
||||
renderTestApp({
|
||||
extensions: [createTestEntityPage({ entity }), myEntityCard],
|
||||
apis: [mockApis.permission()],
|
||||
});
|
||||
```
|
||||
|
||||
### With `TestApiProvider`
|
||||
|
||||
For standalone rendering scenarios where you're not using `renderInTestApp`, the `TestApiProvider` component accepts the same `apis` format:
|
||||
|
||||
```tsx
|
||||
import { render } from '@testing-library/react';
|
||||
import { TestApiProvider, mockApis } from '@backstage/frontend-test-utils';
|
||||
|
||||
render(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
||||
mockApis.alert(),
|
||||
]}
|
||||
>
|
||||
<MyComponent />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
```
|
||||
|
||||
## Plugin-specific test mocks
|
||||
|
||||
Plugins can provide their own mock APIs that follow the same pattern. For example, `@backstage/plugin-catalog-react` provides `catalogApiMock` in its `/testUtils` entry point:
|
||||
|
||||
```tsx
|
||||
import { renderTestApp } from '@backstage/frontend-test-utils';
|
||||
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
|
||||
|
||||
renderTestApp({
|
||||
extensions: [myExtension],
|
||||
apis: [catalogApiMock({ entities: [entity] })],
|
||||
});
|
||||
```
|
||||
|
||||
### Creating your own mock APIs with `attachMockApiFactory`
|
||||
|
||||
If you maintain a plugin that exposes a utility API, you can use `attachMockApiFactory` to create mock instances that can be passed directly to test utilities:
|
||||
|
||||
```ts
|
||||
import { attachMockApiFactory } from '@backstage/frontend-test-utils';
|
||||
import { myApiRef, type MyApi } from '@internal/plugin-example-react';
|
||||
|
||||
export function myApiMock(options?: { greeting?: string }): MyApi {
|
||||
const instance: MyApi = {
|
||||
greet: async () => options?.greeting ?? 'Hello!',
|
||||
};
|
||||
return attachMockApiFactory(myApiRef, instance);
|
||||
}
|
||||
```
|
||||
|
||||
Consumers can then use it like the built-in mocks:
|
||||
|
||||
```tsx
|
||||
await renderInTestApp(<MyComponent />, {
|
||||
apis: [myApiMock({ greeting: 'Hi there!' })],
|
||||
});
|
||||
```
|
||||
|
||||
## Available mock APIs
|
||||
|
||||
The table below lists all core APIs available through the `mockApis` namespace.
|
||||
|
||||
| API | Fake instance | Notes |
|
||||
| --------------------------------- | --------------------- | ---------------------------------------------------------------------- |
|
||||
| `mockApis.alert()` | `MockAlertApi` | Collects alerts; has `getAlerts()`, `clearAlerts()`, `waitForAlert()` |
|
||||
| `mockApis.analytics()` | `MockAnalyticsApi` | Collects events; has `getEvents()` |
|
||||
| `mockApis.config({ data })` | `MockConfigApi` | Reads from a plain JSON object |
|
||||
| `mockApis.discovery({ baseUrl })` | Inline | Returns `${baseUrl}/api/${pluginId}`, defaults to `http://example.com` |
|
||||
| `mockApis.error(options?)` | `MockErrorApi` | Collects errors; has `getErrors()`, `waitForError()` |
|
||||
| `mockApis.featureFlags(options?)` | `MockFeatureFlagsApi` | In-memory flag state; has `getState()`, `setState()`, `clearState()` |
|
||||
| `mockApis.fetch(options?)` | `MockFetchApi` | Wraps `cross-fetch`; supports identity injection and plugin protocol |
|
||||
| `mockApis.identity(options?)` | Inline | Configurable user ref, ownership, token, profile |
|
||||
| `mockApis.permission(options?)` | `MockPermissionApi` | Defaults to `ALLOW`; accepts a handler function |
|
||||
| `mockApis.storage({ data })` | `MockStorageApi` | In-memory storage with bucket support |
|
||||
| `mockApis.translation()` | `MockTranslationApi` | Passthrough returning default messages from translation refs |
|
||||
|
||||
Each of these also has a `.mock()` variant that returns jest mocks, as described above.
|
||||
Reference in New Issue
Block a user