API services
API services are higher-level helpers for Kibana APIs used for setup, teardown, and side-effect verification. They’re accessed via the worker-scoped apiServices fixture (your editor should autocomplete apiServices.<service>).
API services should focus on server-side interactions (HTTP requests). UI interactions belong in page objects.
apiClient: validate the endpoint under test with scoped credentials (see Write API tests).apiServices: prepare state, clean up, or verify side effects with higher privileges.
@kbn/scout ships a small set of built-in services (available as apiServices.<name>):
alertingcasescoredataViewsfleetsampleDataspacesstreams
This list will grow over time. If you build a helper that’s broadly useful, consider contributing it to @kbn/scout (or your solution Scout package) so other teams can reuse it.
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test.describe('My suite', { tag: tags.deploymentAgnostic }, () => {
test.beforeAll(async ({ apiServices }) => {
await apiServices.streams.enable();
});
test.afterAll(async ({ apiServices }) => {
await apiServices.streams.disable();
});
});
If you find repeated API setup/cleanup code across suites, add a helper to your plugin/solution’s apiServices extension. If it’s broadly useful, consider contributing it to @kbn/scout or your solution Scout package.
Guidelines:
- Keep services HTTP-only (wrap
kbnClient.request(...)calls). Put UI flows in page objects. - Make methods read like intent (
enable(),createTestData(),deleteAll()), and keep assertions in tests. - Keep services stateless. Compute per-test ids/names in the test and pass them into the service method.
- If the API is space-scoped, accept an optional
spaceIdand prefix requests with/s/<spaceId>when provided.
-
Define your service
Create a helper that bundles repeated setup/teardown operations (for example
getMyFeatureApiService(...)).If you’ll use it from both UI and API suites, put it under
test/scout/common:// <plugin-root>/test/scout/common/services/my_feature_api_service.ts import type { KbnClient, ScoutLogger } from '@kbn/scout'; export type MyFeatureApiService = { enable: (spaceId?: string) => Promise<void>; disable: (spaceId?: string) => Promise<void>; }; export const getMyFeatureApiService = ({ kbnClient, log, }: { kbnClient: KbnClient; log: ScoutLogger; }): MyFeatureApiService => { const withSpace = (path: string, spaceId?: string) => (spaceId ? `/s/${spaceId}${path}` : path); return { enable: async (spaceId?: string) => { log.debug('Enabling my feature'); await kbnClient.request({ method: 'POST', path: withSpace('/api/my_feature/_enable', spaceId), }); }, disable: async (spaceId?: string) => { log.debug('Disabling my feature'); await kbnClient.request({ method: 'POST', path: withSpace('/api/my_feature/_disable', spaceId), }); }, }; }; -
Extend the
apiServicesfixtureYou can add plugin-specific helpers by extending the
apiServicesfixture in yourfixtures/index.ts.The pattern is the same for UI tests (
test) and API tests (apiTest): extend the entrypoint you import in that suite’sfixtures/index.ts.Example (API tests):
import type { ApiServicesFixture } from '@kbn/scout'; import { apiTest as baseApiTest } from '@kbn/scout'; import { getMyFeatureApiService, type MyFeatureApiService, } from '../../common/services/my_feature_api_service'; export type MyFeatureApiServices = ApiServicesFixture & { myFeature: MyFeatureApiService; }; export const apiTest = baseApiTest.extend<{}, { apiServices: MyFeatureApiServices }>({ apiServices: async ({ apiServices, kbnClient, log }, use) => { await use({ ...apiServices, myFeature: getMyFeatureApiService({ kbnClient, log }), }); }, });Example (UI tests):
import type { ApiServicesFixture } from '@kbn/scout'; import { test as baseTest } from '@kbn/scout'; import { getMyFeatureApiService, type MyFeatureApiService, } from '../../common/services/my_feature_api_service'; export type MyFeatureApiServices = ApiServicesFixture & { myFeature: MyFeatureApiService }; export const test = baseTest.extend<{}, { apiServices: MyFeatureApiServices }>({ apiServices: async ({ apiServices, kbnClient, log }, use) => { await use({ ...apiServices, myFeature: getMyFeatureApiService({ kbnClient, log }) }); }, }); -
Use it for setup/teardown
Use the helper for setup/teardown (and keep the endpoint under test in
apiClientfor readable, scoped tests). See best practices.NoteIn parallel UI suites (
spaceTest), usescoutSpace.idwhen calling methods that need space scoping:await apiServices.myFeature.enable(scoutSpace.id).