Elastic/Kibana插件测试最佳实践指南
概述
Kibana作为Elastic Stack的核心可视化组件,其插件生态系统提供了强大的扩展能力。然而,插件开发过程中的测试环节往往被开发者忽视,导致在生产环境中出现各种难以预料的问题。本文将深入探讨Kibana插件测试的最佳实践,帮助开发者构建稳定可靠的插件应用。
测试金字塔架构
Kibana插件测试遵循经典的测试金字塔模型,从底层到顶层包含:
1. 单元测试(Unit Tests)
单元测试是测试金字塔的基础,专注于测试单个函数、组件或类的独立功能。
Jest配置示例
// jest.config.js
module.exports = {
preset: '@kbn/test',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
moduleNameMapping: {
'\\.(css|less|scss)$': 'identity-obj-proxy',
},
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest'],
},
};
组件测试示例
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MyPluginComponent } from './my_plugin_component';
describe('MyPluginComponent', () => {
it('renders correctly with default props', () => {
render(<MyPluginComponent title="Test Title" />);
expect(screen.getByText('Test Title')).toBeInTheDocument();
});
it('handles click events', () => {
const mockOnClick = jest.fn();
render(<MyPluginComponent onClick={mockOnClick} />);
screen.getByRole('button').click();
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
});
2. 集成测试(Integration Tests)
集成测试验证多个组件或模块之间的协作是否正确。
Plugin Functional Tests
Kibana提供了专门的插件功能测试框架:
// test/plugin_functional/test_suites/my_plugin/index.ts
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const retry = getService('retry');
describe('My Plugin', () => {
it('should load plugin UI', async () => {
await browser.get('/app/myPlugin');
await retry.try(async () => {
expect(await testSubjects.exists('myPluginContainer')).toBe(true);
});
});
it('should display data correctly', async () => {
await testSubjects.click('refreshButton');
await retry.try(async () => {
const dataElement = await testSubjects.find('dataTable');
expect(dataElement).toBeTruthy();
});
});
});
}
3. 功能测试(Functional Tests)
功能测试模拟真实用户操作,验证整个功能流程。
功能测试配置
// test/functional/config.base.js
module.exports = {
services: ['chromium'],
servers: {
kibana: {
protocol: 'http',
hostname: 'localhost',
port: 5620,
},
elasticsearch: {
protocol: 'http',
hostname: 'localhost',
port: 9220,
},
},
junit: {
reportName: 'Kibana Functional Tests',
},
};
测试数据管理
测试夹具(Fixtures)模式
// test/common/fixtures/test_data.ts
export const createTestData = () => ({
indexPattern: 'test-*',
timeRange: {
from: 'now-15m',
to: 'now',
},
aggregations: {
terms: {
field: 'service.name',
},
date_histogram: {
field: '@timestamp',
calendar_interval: '1m',
},
},
});
// 在测试中使用
import { createTestData } from '../fixtures/test_data';
describe('Data Processing', () => {
const testData = createTestData();
it('processes test data correctly', async () => {
const result = await processData(testData);
expect(result).toHaveProperty('aggregations');
});
});
Mocking策略
HTTP请求Mocking
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
const mockHttp = httpServiceMock.createStartContract();
mockHttp.get.mockResolvedValue({
data: [{ id: 1, name: 'Test Item' }],
});
// 在测试中注入mock
const wrapper = ({ children }) => (
<KibanaContextProvider services={{ http: mockHttp }}>
{children}
</KibanaContextProvider>
);
Elasticsearch Client Mocking
import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';
const mockEsClient = elasticsearchServiceMock.createScopedClusterClient();
mockEsClient.asCurrentUser.search.mockResponse({
hits: {
hits: [{ _source: { message: 'test log' } }],
total: { value: 1 },
},
});
测试覆盖率与质量指标
覆盖率配置
{
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/**/__tests__/**",
"!src/**/__mocks__/**"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 85,
"lines": 90,
"statements": 90
}
}
}
质量指标表格
| 测试类型 | 目标覆盖率 | 执行时间要求 | 通过率要求 |
|---|---|---|---|
| 单元测试 | ≥ 90% | < 30秒 | 100% |
| 集成测试 | ≥ 80% | < 2分钟 | 100% |
| 功能测试 | ≥ 70% | < 5分钟 | 95% |
| E2E测试 | ≥ 60% | < 10分钟 | 90% |
持续集成流水线
Jenkins流水线示例
#!/bin/bash
# test/scripts/jenkins_plugin_functional.sh
set -euo pipefail
echo "Starting plugin functional tests..."
# 运行单元测试
yarn test:jest --coverage
# 运行集成测试
yarn test:ftr:runner --config test/plugin_functional/config.ts
# 生成测试报告
yarn cover:report
echo "All tests completed successfully!"
调试技巧与最佳实践
1. 测试调试配置
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "--watch"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
2. 性能优化策略
// 使用异步测试优化
describe('Performance Tests', () => {
beforeEach(async () => {
// 预加载测试数据
await preloadTestData();
});
it('should handle large datasets efficiently', async () => {
const largeDataset = generateLargeDataset(10000);
const startTime = performance.now();
await processLargeDataset(largeDataset);
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(1000); // 1秒内完成
});
});
常见问题与解决方案
问题1:测试环境配置复杂
解决方案:使用Docker容器化测试环境
FROM docker.elastic.co/kibana/kibana:8.13.0
# 复制测试配置
COPY test/ /usr/share/kibana/test/
COPY --chown=kibana:kibana .eslintrc.js /usr/share/kibana/
COPY --chown=kibana:kibana .prettierrc.js /usr/share/kibana/
# 安装测试依赖
RUN bin/kibana-plugin install --dev
问题2:测试数据管理困难
解决方案:实现测试数据工厂模式
class TestDataFactory {
private static instance: TestDataFactory;
static getInstance(): TestDataFactory {
if (!TestDataFactory.instance) {
TestDataFactory.instance = new TestDataFactory();
}
return TestDataFactory.instance;
}
createIndexPattern(config: Partial<IndexPatternConfig> = {}) {
return {
title: config.title || 'test-*',
timeFieldName: config.timeFieldName || '@timestamp',
...config,
};
}
}
总结
Kibana插件测试是一个系统工程,需要从单元测试到端到端测试的全方位覆盖。通过遵循本文介绍的最佳实践,开发者可以:
- 建立完整的测试金字塔,确保各层级测试的适当覆盖
- 实施有效的Mocking策略,提高测试的稳定性和可维护性
- 优化测试性能,在CI/CD流水线中快速反馈测试结果
- 管理测试数据,确保测试的一致性和可重复性
记住,良好的测试实践不仅是质量保证的手段,更是团队协作和项目可持续发展的基石。投入时间建立完善的测试体系,将在项目的整个生命周期中带来丰厚的回报。
下一步行动建议:
- 评估现有插件的测试覆盖率
- 制定测试改进计划
- 建立持续集成流水线
- 定期进行测试代码审查
通过系统化的测试实践,您的Kibana插件将更加稳定、可靠,为用户提供更好的体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



