Skip to main content

测试后端插件和模块

用于测试后端插件和模块的实用程序可在@backstage/backend-test-utils本节将介绍这些设施。

测试后端插件和模块

为便于测试后端插件和模块,可使用@backstage/backend-test-utils软件包提供了一个startTestBackend函数,它可以启动整个后端工具包,其中包含大量模拟服务。 然后,您可以为需要在测试运行中调整其行为的服务提供重载。 该函数还接受大量的特点(后端)的统称插件模块),它们都是测试对象。

该函数返回一个 HTTP 服务器实例,可与诸如supertest的路由注册的插件的实际 REST 服务界面进行测试。HTTP 路由器服务 API.

import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import request from 'supertest';
import { myPlugin } from './plugin.ts';

describe('myPlugin', () => {
it('can serve values from config', async () => {
const fakeConfig = { myPlugin: { value: 7 } };

const { server } = await startTestBackend({
features: [
myPlugin(),
mockServices.rootConfig.factory({ data: fakeConfig }),
],
});

const response = await request(server).get('/api/example/get-value');
expect(response.status).toBe(200);
expect(response.body).toEqual({ value: 7 });
});
});

本例展示了如何访问模拟服务工厂并向其传递选项,从而覆盖默认的模拟服务。

返回的服务器也有一个port()方法,该方法会返回动态绑定的监听端口。 您可以使用该方法与运行中的测试服务进行较低级别的网络交互。

测试远程服务交互

如果您的后端插件或服务使用 HTTP 调用与外部服务交互,我们建议您利用msw软件包拦截实际发出的请求并返回模拟响应。 这样,您就可以对远程服务而不是本地客户端进行存根测试,从而实现更全面、更强大的测试。 您可以阅读更多有关其工作原理的信息在其文件中.

@backstage/backend-test-utils软件包导出一个setupRequestMockHandlers函数,该函数可确保正确的jest生命周期钩子被调用,以设置和删除您的msw这样可以确保您的测试不会从测试中意外泄漏流量到生产中。

例如

import { setupRequestMockHandlers } from '@backstage/backend-test-utils';
import { rest } from 'msw';
import { setupServer } from 'msw/node';

describe('read from remote', () => {
const worker = setupServer();
setupRequestMockHandlers(worker);

it('should auth and read successfully', async () => {
expect.assertions(1);

worker.use(
rest.get('https://remote-server.com/api/v3/foo', (req, res, ctx) => {
expect(req.headers.get('authorization')).toBe('Bearer fake');
return res(
ctx.status(200),
ctx.set('Content-Type', 'application/json'),
ctx.body(JSON.stringify({ value: 7 })),
);
}),
);

// exercise your plugin or service as usual, with real clients
});
});

测试数据库交互

@backstage/backend-test-utils软件包包含测试插件与数据库交互的功能,包括启动testcontainers由 Docker 映像提供动力,可连接到真正的数据库引擎。

这种测试的基本设置如下:

// MyDatabaseClass.test.ts
import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
import { MyDatabaseClass, type FooTableRow } from './MyDatabaseClass';

describe('MyDatabaseClass', () => {
// Change this to the set of constants that you actually actively intend to
// support. This create call must be made inside a describe block. Make sure
// to create only one TestDatabases instance per file, since spinning up
// "physical" databases to test against is much costlier than creating the
// "logical" databases within them that the individual tests use.
const databases = TestDatabases.create({
ids: ['POSTGRES_16', 'POSTGRES_12', 'SQLITE_3', 'MYSQL_8'],
});

// Just an example of how to conveniently bundle up the setup code
async function createSubject(databaseId: TestDatabaseId) {
const knex = await databases.init(databaseId);
const subject = new MyDatabaseClass({ database: knex });
await subject.runMigrations();
return { knex, subject };
}

describe('foo', () => {
// Easily run the exact same test onto all supported databases
it.each(databases.eachSupportedId())(
'should run foo on %p',
async databaseId => {
const { knex, subject } = await createSubject(databaseId);
// raw knex is available for underlying manipulation
await knex<FooTableRow>('foo').insert({ value: 2 });
// drive your system under test as usual
await expect(subject.foos()).resolves.toEqual([{ value: 2 }]);
});
});

如果要将测试数据库实例传递给后端插件或服务,可以以模拟实例的形式提供coreServices.database到测试数据库中。

const { knex, subject } = await createSubject(databaseId);
const { server } = await startTestBackend({
features: [
myPlugin(),
mockServices.database.mock({ getClient: async () => knex }),
],
});

在本地运行时,为了提高速度,测试只针对 SQLite 运行。CI环境变量被设置后,将使用所有给定的数据库引擎。

如果你不希望或无法使用基于 docker 的数据库引擎,例如,如果你的 CI 环境能够原生提供数据库,那么可以使用TestDatabases通过使用环境变量支持自定义连接字符串。

  • backstage_test_database_postgres13_connection_string * backstage_test_database_postgres9_connection_string * backstage_test_database_mysql8_connection_string

测试服务工厂

为便于服务工厂的测试@backstage/backend-test-utils软件包提供了一个ServiceFactoryTester助手,让你能在受控的上下文中实例化服务。

下面的示例展示了如何测试一个服务工厂,其中我们还提供了一个模拟的rootConfig服务。

import {
mockServices,
ServiceFactoryTester,
} from '@backstage/backend-test-utils';
import { myServiceFactory } from './myServiceFactory.ts';

describe('myServiceFactory', () => {
it('should provide value', async () => {
const fakeConfig = { myConfiguredValue: 7 };

const tester = ServiceFactoryTester.from(myServiceFactory, {
dependencies: [mockServices.rootConfig.factory({ data: fakeConfig })],
});

const myService = await tester.get('test-plugin');

expect(myService.getValue()).toBe(7);
});
});

默认情况下,服务工厂测试器还提供所有核心服务中大部分服务的模拟实现。