测试后端插件和模块
用于测试后端插件和模块的实用程序可在@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);
});
});
默认情况下,服务工厂测试器还提供所有核心服务中大部分服务的模拟实现。