测试

编辑

运行特定的 Kibana 测试

编辑

下表概述了可能的测试文件位置以及如何调用它们

测试运行器 测试位置 运行器命令(工作目录为 Kibana 根目录)

Jest

**/*.test.{js,mjs,ts,tsx}

yarn test:jest [测试路径]

Jest (集成)

**/integration_tests/**/*.test.{js,mjs,ts,tsx}

yarn test:jest_integration [测试路径]

功能测试

test/**/config.js x-pack/test/**/config.js

node scripts/functional_tests_server --config [目录]/config.js node scripts/functional_test_runner --config [目录]/config.js --grep=regexp

测试运行器参数:- 如果适用,可选参数 --grep=regexp 将仅运行描述与正则表达式匹配的测试或测试套件。 - [测试路径] 是测试文件的相对路径。

单元测试

编辑

Kibana 主要使用 Jest 进行单元测试。每个插件或包都定义了一个 jest.config.js,它扩展了 @kbn/test 包提供的预设。除非您打算运行项目中的所有单元测试,否则为要测试的插件或包提供 Jest 配置文件是最有效的。

yarn jest --config src/plugins/dashboard/jest.config.js

有一个脚本可用于在整个存储库中导航时提供更好的测试用户体验。要运行当前工作目录中的测试,请使用 yarn test:jest。与 Jest CLI 一样,您还可以提供路径以确定要运行的测试。

kibana/src/plugins/dashboard/server$ yarn test:jest #or
kibana/src/plugins/dashboard$ yarn test:jest server #or
kibana$ yarn test:jest src/plugins/dashboard/server

提供给 test:jest 的任何其他选项都将传递给 Jest CLI,并且始终会输出生成的 Jest 命令。

kibana/src/plugins/dashboard/server$ yarn test:jest --coverage

# is equivalent to

yarn jest --coverage --verbose --config /home/tyler/elastic/kibana/src/plugins/dashboard/jest.config.js server

您可以为单个插件生成代码覆盖率报告。

yarn jest --coverage --config src/plugins/console/jest.config.js

Html 报告位于 target/kibana-coverage/jest/path/to/plugin 中

运行浏览器自动化测试

编辑

查看功能测试,了解有关如何为 Kibana 核心和插件运行和开发功能测试的更多信息。

您还可以查看 Scripts README.md,了解有关使用我们提供的 node 脚本来构建 Kibana、运行集成测试以及在您开发时启动 Kibana 和 Elasticsearch 的更多信息。

更多测试信息
编辑

功能测试

编辑

我们使用功能测试来确保 Kibana UI 按预期工作。它通过自动化用户交互来取代数小时的手动测试。为了更好地控制我们的功能测试环境,并使其更易于插件作者访问,Kibana 使用了一个名为 FunctionalTestRunner 的工具。

运行功能测试
编辑

FunctionalTestRunner (FTR) 非常简单,其大部分功能都来自其配置文件。Kibana 存储库包含许多 FTR 配置文件,这些文件对 Kibana 服务器或 Elasticsearch 使用略有不同的配置,具有不同的测试文件,并且可能还有其他配置差异。FTR 配置文件根据测试区域和分发类型在清单文件中组织: 无服务器: - ftr_base_serverless_configs.yml - ftr_oblt_serverless_configs.yml - ftr_security_serverless_configs.yml - ftr_search_serverless_configs.yml 有状态: - ftr_platform_stateful_configs.yml - ftr_oblt_stateful_configs.yml - ftr_security_stateful_configs.yml - ftr_search_stateful_configs.yml 如果您在 Kibana 存储库之外编写插件,您将拥有自己的配置文件。有关更多信息,请参阅Kibana 存储库之外的插件的功能测试

根据您的目标,有三种运行测试的方法

  1. 最简单选项

    • 描述:启动 Kibana 和 Elasticsearch 服务器,然后运行测试。当多次运行测试时,这会慢得多,因为服务器的启动时间很慢。建议单次运行。
    • node scripts/functional_tests

      • 在单个命令中完成所有操作,包括在本地运行 Elasticsearch 和 Kibana
      • 在测试运行后销毁所有内容
      • 退出代码报告测试的成功/失败
  2. 最适合开发

    • 描述:两个命令,在单独的终端中运行,将长期运行且缓慢的组件与短暂且快速的组件分开。可以更快地重新运行测试,并且这仍然在本地运行 Elasticsearch 和 Kibana。
    • node scripts/functional_tests_server

      • 启动 Elasticsearch 和 Kibana 服务器
      • 启动速度慢
      • 可以重复用于多次执行测试,从而在重新运行测试时节省一些时间
      • 当检测到相关更改时,会自动重启 Kibana 服务器
    • node scripts/functional_test_runner

      • 针对由 node scripts/functional_tests_server 启动的 Kibana 和 Elasticsearch 服务器运行测试
      • 退出代码报告测试的成功或失败
  3. 自定义选项

    • 描述:针对以其他方式启动的 Elasticsearch 和 Kibana 实例运行测试(例如 Elastic Cloud,或者您以其他方式管理的实例)。
    • 仅执行功能测试
    • Elasticsearch 和 Kibana 的 URL、凭据等通过环境变量指定
    • 当针对 Elastic Cloud 实例运行时,需要额外的环境变量 TEST_CLOUDES_SECURITY_ENABLED
    • 您必须运行与您正在测试的 Kibana 版本相同的测试分支。要针对以前的次要版本运行,请使用选项 --es-version <实例版本>
    • 要运行特定的配置,请使用选项 --config <配置文件>
    • 以下是一个针对 Elastic Cloud 实例运行的示例

      export TEST_KIBANA_URL=https://elastic:[email protected]:443
      export TEST_ES_URL=https://elastic:[email protected]:443
      
      export TEST_CLOUD=1
      export ES_SECURITY_ENABLED=1
      
      node scripts/functional_test_runner [--config <config>] [--es-version <instance version>]
    • 或者您可以覆盖 URL 的任何或所有这些单独部分,而将其余部分保留为默认值。

      export TEST_KIBANA_PROTOCOL=https
      export TEST_KIBANA_HOSTNAME=my-kibana-instance.internal.net
      export TEST_KIBANA_PORT=443
      export TEST_KIBANA_USER=kibana
      export TEST_KIBANA_PASS=<password>
      
      export TEST_ES_PROTOCOL=http
      export TEST_ES_HOSTNAME=my-es-cluster.internal.net
      export TEST_ES_PORT=9200
      export TEST_ES_USER=elastic
      export TEST_ES_PASS=<password>
      node scripts/functional_test_runner
    • Selenium 测试在 CI 上以无头模式运行。在本地,相同的测试将在真实的浏览器中执行。您可以通过设置环境变量来激活无头模式

      export TEST_BROWSER_HEADLESS=1
    • 如果您正在使用 Google Chrome,您可以减慢本地网络连接速度以验证测试稳定性

      export TEST_THROTTLE_NETWORK=1
    • 当针对 Cloud 部署运行时,某些测试不适用。要跳过不适用的测试,请使用 --exclude-tag。

      node scripts/functional_test_runner --exclude-tag skipCloud
      node scripts/functional_test_runner --exclude-tag skipMKI
更多关于 node scripts/functional_test_runner 的信息
编辑

在没有任何参数的情况下运行时,FunctionalTestRunner 会自动加载标准位置的配置,但是您可以使用 --config 标志覆盖该行为。使用多个 --config 参数列出配置。

  • --config test/functional/apps/app-name/config.js 使用配置为在 Chrome 中运行特定应用程序的 WebDriver 测试启动 Elasticsearch 和 Kibana 服务器。例如,--config test/functional/apps/home/config.js 使用配置为在 Chrome 中运行 home 应用程序的 WebDriver 测试启动 Elasticsearch 和 Kibana 服务器。
  • --config test/functional/config.firefox.js 使用配置为在 Firefox 中运行的 WebDriver 测试启动 Elasticsearch 和 Kibana 服务器。
  • --config test/api_integration/config.js 使用 api 集成测试配置启动 Elasticsearch 和 Kibana 服务器。
  • --config test/accessibility/config.ts 使用配置为使用 axe 运行可访问性审计的 WebDriver 测试启动 Elasticsearch 和 Kibana 服务器。

还有 --bail--grep 的命令行标志,它们的行为与它们的 mocha 对应项相同。例如,使用 --grep=foo 仅运行与正则表达式匹配的测试。

也可以使用 --quiet--debug--verbose 标志自定义日志记录。

还有 --include 之类的选项,仅运行单个文件或一组文件中定义的测试。

运行 node scripts/functional_test_runner --help 以查看所有可用选项。

编写功能测试
编辑
环境
编辑

测试是用 mocha 编写的,使用 @kbn/expect 进行断言。

我们使用 WebDriver 协议 在 Chrome 和 Firefox 中运行测试,并借助 chromedrivergeckodriver。当 FunctionalTestRunner 启动时,远程服务会创建一个新的 webdriver 会话,该会话将启动驱动程序和一个精简的浏览器实例。我们使用 browser 服务和 webElementWrapper 类来包装 Webdriver API

FunctionalTestRunner 使用 babel 自动转换功能测试,以便测试可以使用与 Kibana 源代码相同的 ECMAScript 功能。请参阅 STYLEGUIDE.mdx

定义
编辑

提供程序

FunctionalTestRunner 运行的代码包装在一个函数中,以便可以通过配置文件传递它并进行参数化。任何这些 Provider 函数都可以是异步的,并且应该返回/解析为它们要提供的值。Provider 函数将始终使用单个参数调用:提供程序 API(请参阅Provider API 部分)。

配置提供程序

// config and test files use `export default`
export default function (/* { providerAPI } */) {
  return {
    // ...
  }
}
服务
服务是使用 FtrService 的子类创建的命名单例。测试和其他服务可以通过按名称请求来检索服务实例。除了 mocha API 之外的所有功能都通过服务公开。当您编写自己的功能测试时,请检查是否有现有的服务可以帮助您执行的交互,并为尚未在服务中编码的交互添加新服务。
服务提供程序
出于遗留目的,以及当创建 FtrService 的子类不方便时,您还可以使用“服务提供程序”创建服务。这些是创建服务实例并返回它们的函数。这些实例被缓存并提供给测试。目前,这些提供程序也可以返回服务实例的 Promise,允许服务在测试运行之前进行一些设置工作。我们希望在不久的将来完全弃用并删除对异步服务提供程序的支持,而是要求服务使用 lifecycle 服务在测试之前运行设置。返回 FtrService 以外类的实例的提供程序很可能在尽可能长的时间内得到支持。
页面对象
页面对象在功能上等同于服务,只是它们使用略有不同的机制加载,并且通常与服务分开定义。当您编写自己的功能测试时,您可能需要将某些服务编写为页面对象,但这不是必需的。
测试文件
FunctionalTestRunner 的主要目的是执行测试文件。这些文件导出一个测试提供程序,该程序会使用 Provider API 调用,但不应返回值。相反,测试提供程序使用 mocha 的 BDD 接口定义一个套件。
测试套件
测试套件是通过调用 describe() 定义的测试集合,然后通过调用 it()before()beforeEach() 等来填充测试和设置/拆卸钩子。每个测试文件必须只定义一个顶级测试套件,并且测试套件可以根据需要拥有任意多个嵌套的测试套件。
标签

describe() 函数中使用标签来对功能测试进行分组。标签包括

  • ciGroup{id} - 将测试套件分配给特定的 CI 工作程序
  • skipCloudskipFirefox - 将测试套件排除在云端或 Firefox 上运行
  • includeFirefox - 将在 Chrome 和 Firefox 上运行的测试分组
跨浏览器测试
在 CI 上,默认情况下所有功能测试都在 Chrome 中执行。要也在 Firefox 上运行一个套件,请分配 includeFirefox 标签
// on CI test suite will be run twice: in Chrome and Firefox
describe('My Cross-browser Test Suite', function () {
  this.tags('includeFirefox');

  it('My First Test');
}

如果测试不适用于 Firefox,请分配 skipFirefox 标签。

要在本地的 Firefox 上运行测试,请使用 config.firefox.js

node scripts/functional_test_runner --config test/functional/config.firefox.js
使用 test_user 服务
编辑

测试应在正面的安全边界条件下运行,这意味着它们应使用所需的最小权限(和文档记录)运行,而不是以超级用户身份运行。这可以防止出现意外需要额外权限才能执行相同操作的回归类型。

功能 UI 测试现在默认使用名为 test_user 的用户登录,并且可以在不登录和退出的情况下动态更改此用户的角色。

为了实现这一点,引入了一个名为 createTestUserService 的新服务(请参阅 packages/kbn-ftr-common-functional-ui-services/services/security/test_user.ts)。此测试用户服务的目的是创建测试配置文件中定义的角色,并设置 setRoles() 或 restoreDefaults()。

以下是如何设置角色的示例,如下所示

await security.testUser.setRoles(['kibana_user', 'kibana_date_nanos']);

在这里,我们将 test_user 设置为具有 kibana_user 角色,并且具有对特定数据索引 (kibana_date_nanos) 的角色访问权限。

测试通常应在 before() 中设置 setRoles(),并在 after() 中设置 restoreDefaults()。

测试文件结构
编辑

此带注释的示例文件显示了每个测试套件使用的基本结构。它首先导入 @kbn/expect 并定义其默认导出:一个匿名的测试提供程序。然后,测试提供程序为 getService()getPageObjects() 函数解构 Provider API。它使用这些函数来收集此套件的依赖项。测试文件的其余部分对于 mocha.js 用户来说看起来很正常。describe()it()before() 以及其他函数用于定义通过服务和 PageObject 类型的对象来自动化浏览器的套件。

import expect from '@kbn/expect';
// test files must `export default` a function that defines a test suite
export default function ({ getService, getPageObject }) {

  // most test files will start off by loading some services
  const retry = getService('retry');
  const testSubjects = getService('testSubjects');
  const esArchiver = getService('esArchiver');
  const kibanaServer = getService('kibanaServer');

  // for historical reasons, PageObjects are loaded in a single API call
  // and returned on an object with a key/value for each requested PageObject
  const PageObjects = getPageObjects(['common', 'visualize']);

  // every file must define a top-level suite before defining hooks/tests
  describe('My Test Suite', () => {

    // most suites start with a before hook that navigates to a specific
    // app/page and restores some archives into {es} with esArchiver
    before(async () => {
      await Promise.all([
        // start by clearing Saved Objects from the .kibana index
        await kibanaServer.savedObjects.cleanStandardList();
        // load some basic log data only if the index doesn't exist
        esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/makelogs')
      ]);
      // go to the page described by `apps.visualize` in the config
      await PageObjects.common.navigateTo('visualize');
    });

    // right after the before() hook definition, add the teardown steps
    // that will tidy up {es} for other test suites
    after(async () => {
      // we clear Kibana Saved Objects but not the makelogs
      // archive because we don't make any changes to it, and subsequent
      // suites could use it if they call `.loadIfNeeded()`.
      await kibanaServer.savedObjects.cleanStandardList();
    });

    // This series of tests illustrate how tests generally verify
    // one step of a larger process and then move on to the next in
    // a new test, each step building on top of the previous
    it('Vis Listing Page is empty');
    it('Create a new vis');
    it('Shows new vis in listing page');
    it('Opens the saved vis');
    it('Respects time filter changes');
    it(...
  });

}
Provider API
编辑

所有提供程序的第一个也是唯一的参数是 Provider API 对象。此对象可用于加载服务/页面对象和配置/测试文件。

在配置文件中,API 具有以下属性

log

ToolingLog 的实例,已准备好使用

readConfigFile(path)

返回一个 Promise,该 Promise 将解析为 Config 实例,该实例提供 path 处配置文件的值

在服务和 PageObject 提供程序中,API 为

getService(name)

按名称加载并返回服务的单例实例

getPageObjects(names)

加载 PageObject 的单例实例,并将它们收集到对象上,其中每个名称都是该 PageObject 单例实例的键

在测试提供程序中,API 与服务提供程序 API 完全相同,但有一个附加方法

loadTestFile(path)

就地加载路径处的测试文件。使用此方法将其他文件中的套件嵌套到更高级别的套件中

服务索引
编辑
内置服务
编辑

FunctionalTestRunner 附带三个内置服务

config
  • 使用 config.get(path) 从配置文件中读取任何值
log
  • ToolingLog 实例是可读流。此服务提供的实例由 FunctionalTestRunner CLI 自动管道传输到 stdout
  • log.verbose()log.debug()log.info()log.warning() 的工作方式与 console.log 完全相同,但会产生更有组织的输出
lifecycle
  • 主要用于服务中
  • 公开用于基本协调的生命周期事件。处理程序可以返回 Promise 并异步解析/失败
  • 阶段包括:beforeLoadTestsbeforeTestsbeforeEachTestcleanup
Kibana 服务
编辑

Kibana 功能测试定义了测试使用的大部分实际功能。

browser
  • remote 服务的高级封装,它公开了可用的浏览器操作
  • 常用方法

    • browser.getWindowSize()
    • browser.refresh()
testSubjects
  • 测试主题是专门标记的元素,用于从测试中选择
  • 尽可能使用 testSubjects 而不是 CSS 选择器
  • 用法

    • 使用 data-test-subj 属性标记您的测试主题

      <div id="container”>
        <button id="clickMe” data-test-subj=”containerButton” />
      </div>
    • 使用 testSubjects 帮助程序单击此按钮

      await testSubjects.click(‘containerButton’);
  • 常用方法

    • testSubjects.find(testSubjectSelector) - 在页面中查找测试主题;如果在一段时间后找不到,则抛出异常
    • testSubjects.click(testSubjectSelector) - 单击页面中的测试主题;如果在一段时间后找不到,则抛出异常
find
  • 用于记录和管理超时的 remote.findBy* 方法的帮助程序
  • 常用方法

    • find.byCssSelector()
    • find.allByCssSelector()
retry
  • 用于重试操作的帮助程序
  • 常用方法

    • retry.try(fn, onFailureBlock) - 在循环中执行 fn,直到它成功或经过默认超时时间为止。可选的 onFailureBlock 在每次重试尝试之前执行。
    • retry.tryForTime(ms, fn, onFailureBlock) - 在循环中执行 fn,直到它成功或经过 ms 毫秒为止。可选的 onFailureBlock 在每次重试尝试之前执行。
kibanaServer
  • 用于与 Kibana 服务器交互的帮助程序
  • 常用方法

    • kibanaServer.uiSettings.update()
    • kibanaServer.version.get()
    • kibanaServer.status.getOverallState()
esArchiver
  • 加载/卸载使用 esArchiver 创建的存档
  • 常用方法

    • esArchiver.load(path)
    • esArchiver.loadIfNeeded(path)
    • esArchiver.unload(path)

可以在此处找到功能测试中使用的所有服务列表:test/functional/services

底层实用程序
  • es

    • Elasticsearch 客户端
    • 更高级别的选项:kibanaServer.uiSettingsesArchiver
  • remote

    • WebDriver 类的实例
    • 负责与浏览器的所有通信
    • 要执行浏览器操作,请使用 remote 服务
    • 要搜索和操作 DOM 元素,请使用 testSubjectsfind 服务
    • 请参阅 selenium-webdriver 文档以获取完整的 API。
自定义服务
编辑

服务是有意为通用的。它们可以是字面上的任何东西(甚至什么都不是)。某些服务具有用于与特定类型 UI 元素(如 pointSeriesVis)交互的帮助程序,而另一些服务则更基础,如 logconfig。当您想在可重用包中提供某些功能时,请考虑创建自定义服务。

要创建自定义服务 somethingUseful

  • 创建一个类似于此的 test/functional/services/something_useful.js 文件

    // Services are defined by Provider functions that receive the ServiceProviderAPI
    export function SomethingUsefulProvider({ getService }) {
      const log = getService('log');
    
      class SomethingUseful {
        doSomething() {
        }
      }
      return new SomethingUseful();
    }
  • services/index.js 重新导出您的提供程序
  • 将其导入到 src/functional/config.base.js 并将其添加到服务配置中

    import { SomethingUsefulProvider } from './services';
    
    export default function () {
      return {
        // … truncated ...
        services: {
          somethingUseful: SomethingUsefulProvider
        }
      }
    }
PageObjects
编辑

每个 PageObject 的目的都非常不言自明。可视化 PageObject 提供了用于与可视化应用程序交互的帮助程序,仪表板对于仪表板应用程序来说是相同的,依此类推。

一个例外是“common” PageObject。作为 intern 实现的遗留问题,“common” PageObject 是跨页面有用的帮助程序集合。既然我们有了可共享的服务,并且这些服务可以与其他 FunctionalTestRunner 配置共享,我们将继续将功能从 common PageObject 移到服务中。

请将新方法添加到现有或新服务,而不是进一步扩展 CommonPage 类。

陷阱
编辑

请记住,您不能在文件中运行单个测试(it 块),因为需要按顺序运行整个 describe。一个文件中应该只有一个顶级 describe

功能测试时间
编辑

另一个需要注意的重要问题是通过注意时间来编写稳定的测试。 remote 上的所有方法都是异步运行的。最好编写交互,等待 UI 上出现更改后再进行下一步操作。

例如,与其编写一个简单地单击按钮的交互,不如编写一个具有更高层次目标的交互

错误示例:PageObjects.app.clickButton()

class AppPage {
  // what can people who call this method expect from the
  // UI after the promise resolves? Since the reaction to most
  // clicks is asynchronous the behavior is dependent on timing
  // and likely to cause test that fail unexpectedly
  async clickButton () {
    await testSubjects.click(‘menuButton’);
  }
}

正确示例:PageObjects.app.openMenu()

class AppPage {
  // unlike `clickButton()`, callers of `openMenu()` know
  // the state that the UI will be in before they move on to
  // the next step
  async openMenu () {
    await testSubjects.click(‘menuButton’);
    await testSubjects.exists(‘menu’);
  }
}

以这种方式编写可以确保您的测试时间不会出现不稳定的情况,或者基于交互后 UI 更新的假设。

调试
编辑

从命令行运行

node --inspect-brk scripts/functional_test_runner

这将打印出一个 URL,您可以在 Chrome 中访问该 URL 并调试浏览器中的功能测试。

您还可以通过使用 --debug--verbose 标志运行 FunctionalTestRunner 在终端中查看其他日志。 使用测试中的语句添加更多日志,例如

// load the log service
const log = getService(‘log’);

// log.debug only writes when using the `--debug` or `--verbose` flag.
log.debug(‘done clicking menu’);
MacOS 测试性能提示
编辑

在具有独立显卡的机器上的 macOS 用户可以通过更改终端模拟器的 GPU 设置来显著提高测试速度(高达 2 倍)。在 iTerm2 中:* 打开首选项(Command + ,) * 在“常规”选项卡中,“魔术”部分下,确保选中“GPU 渲染” * 打开“高级 GPU 设置…” * 取消选中“优先使用集成显卡而不是独立显卡”选项 * 重启 iTerm

不稳定测试运行器

编辑

如果您的功能测试不稳定,那么运营团队可能会跳过它们,并要求您在再次启用它们之前使其不那么不稳定。此过程通常包括查看相关 Github 问题中记录的失败情况,并查找需要在测试中某个时刻等待的不正确的假设或条件。为了确定您的更改是否降低了测试失败的频率,您可以在不稳定测试运行器中运行测试。此工具最多运行特定 ciGroup 的 500 次执行。要启动不稳定测试运行器的构建,请使用您的更改创建一个 PR,然后访问 https://ci-stats.kibana.dev/trigger_flaky_test_runner,选择您的 PR,选择您的测试所在的 CI 组,然后触发构建。

这将带您到 Buildkite,您的构建将在其中运行,并告诉您是否在任何执行中失败。

一个不稳定的测试可能只在 1000 次运行中失败一次,因此请记住这一点,并确保您使用足够的执行次数来真正证明测试不再不稳定。

单元测试框架

编辑

Kibana 使用 Jest 进行单元测试。

Jest

编辑

Jest 测试存储在与源代码文件相同的目录中,后缀为 .test.{js,mjs,ts,tsx}

每个插件和包都包含自己的 jest.config.js 文件来定义其根目录,以及对 @kbn/test 提供的 jest 预设的任何覆盖。在处理单个插件或包时,您会发现运行 Jest 时提供配置文件会更有效。

yarn jest --config src/plugins/discover/jest.config.js
编写 Jest 单元测试
编辑

为了编写这些测试,您需要注意两件事。第一个是 jest.mockjest.doMock 之间的区别,第二个是我们的 jest 模拟文件模式。由于我们使用 babel-jest 运行 jsts 测试文件,因此两种技术都是必需的,特别是对于在 Typescript 中实现的测试,以利用自动类型推断功能。

Jest.mock 与 Jest.doMock
编辑

这两种方法本质上是相同的,但是 jest.mock 调用将被提升到文件顶部,并且只能引用以 mock 为前缀的变量。另一方面,jest.doMock 不会被提升,并且可以引用我们想要的几乎任何变量,但是我们必须确保这些引用的变量在我们需要的时刻实例化,这使我们进入下一节,我们将讨论我们的 jest 模拟文件模式。

Jest 模拟文件模式
编辑

特别是在 typescript 中,在单元测试中非常常见的是 jest.doMock 调用,该调用引用例如导入的类型。执行此操作将引发任何错误,但是测试将失败。原因在于,尽管 jest.doMock 不会被 babel-jest 提升,但是我们引用的类型的导入将被提升到顶部,并且在我们调用该函数时,该变量将不会被定义。

为了防止这种情况发生,我们制定了一项协议,应遵循该协议

  • 每个模块都可以在 mymodule.mock.ts 中提供标准模拟,以防其他测试可以从此处使用定义中受益。此文件将没有任何 jest.mock 调用,只有虚拟对象。
  • 每个测试都在 mymodule.test.mocks.ts 中定义其模拟。此文件可以从通用模块的模拟文件 (*.mock.ts) 中导入相关的模拟,并为每个模拟调用 jest.mock。如果有任何相关的虚拟模拟对象需要通用化(并供其他测试使用),则可以直接在此文件中定义虚拟对象。
  • 每个测试都将从测试模拟文件 mymodule.test.mocks.ts 中导入其模拟。 mymodule.test.ts 的导入如下:import * as Mocks from './mymodule.test.mocks'import { mockX } from './mymodule.test.mocks' 或仅 import './mymodule.test.mocks'(如果没有导出任何要使用的内容)。
调试单元测试
编辑

标准的 yarn test 任务运行多个子任务,可能需要几分钟才能完成,这使得调试失败非常痛苦。为了减轻痛苦,专门的任务提供了运行测试的替代方法。

您还可以添加 --debug 选项,以便使用 --inspect-brk 标志运行 node。您需要连接一个远程调试器,例如 node-inspector,才能在此模式下继续。

单元测试插件
编辑

即使在使用 Kibana 插件生成器 时,我们也没有强制要求对插件进行单元测试的方法。请自行设置并使用您选择的工具。如果该插件将位于 Kibana 代码库中,则必须使用 Jest

自动辅助功能测试

编辑

要编写辅助功能测试,请使用提供的辅助功能服务 getService('a11y')。辅助功能测试的编写非常简单,因为 axe 完成了大部分繁重的工作。导航到您需要测试的 UI,然后从先前导入的服务中调用 testAppSnapshot();,以确保 axe 未发现任何故障。浏览 UI 的每个部分以获得最佳覆盖率。

一个示例测试可能如下所示

export default function ({ getService, getPageObjects }) {
  const { common, home } = getPageObjects(['common', 'home']);
  const a11y = getService('a11y'); /* this is the wrapping service around axe */

  describe('Kibana Home', () => {
    before(async () => {
      await common.navigateToApp('home'); /* navigates to the page we want to test */
    });

    it('Kibana Home view', async () => {
      await retry.waitFor(
        'home page visible',
        async () => await testSubjects.exists('homeApp')
      ); /* confirm you're on the correct page and that it's loaded */
      await a11y.testAppSnapshot(); /* this expects that there are no failures found by axe */
    });

    /**
     * If these tests were added by our QA team, tests that fail that require significant app code
     * changes to be fixed will be skipped with a corresponding issue label with more info
     */
    // Skipped due to https://github.com/elastic/kibana/issues/99999
    it.skip('all plugins view page meets a11y requirements', async () => {
      await home.clickAllKibanaPlugins();
      await a11y.testAppSnapshot();
    });

    /**
     * Testing all the versions and different views of of a page is important to get good
     * coverage. Things like empty states, different license levels, different permissions, and
     * loaded data can all significantly change the UI which necessitates their own test.
     */
    it('Add Kibana sample data page', async () => {
      await common.navigateToUrl('home', '/tutorial_directory/sampleData', {
        useActualUrl: true,
      });
      await a11y.testAppSnapshot();
    });
  });
}

运行测试

编辑

要在本地运行测试

  1. 在一个终端窗口中运行

    node scripts/functional_tests_server --config test/accessibility/config.ts
  2. 当服务器打印出它已准备就绪时,在另一个终端窗口中运行

    node scripts/functional_test_runner.js --config test/accessibility/config.ts

要运行 x-pack 测试,请将配置文件替换为 x-pack/test/accessibility/apps/{group1,group2,group3}/config.ts

测试是使用 axe 完成的。您可以使用浏览器插件运行与 CI 运行的相同内容

失败分析

编辑

如果您以前从未见过失败,则失败可能会让人感到困惑。以下是来自 CI 的失败可能看起来的分解

1)    Dashboard
       create dashboard button:

      Error: a11y report:

 VIOLATION
   [aria-hidden-focus]: Ensures aria-hidden elements do not contain focusable elements
     Help: https://dequeuniversity.com/rules/axe/3.5/aria-hidden-focus?application=axeAPI
     Elements:
       - <span aria-hidden="true"><button type="button">Submit</button></span>
       at Accessibility.testAxeReport (test/accessibility/services/a11y/a11y.ts:90:15)
       at Accessibility.testAppSnapshot (test/accessibility/services/a11y/a11y.ts:58:18)
       at process._tickCallback (internal/process/next_tick.js:68:7)
  • “Dashboard”和“create dashboard button”是失败的测试套件和特定测试的名称。
  • 始终在括号中,“[aria-hidden-focus]”是失败的 axe 规则的名称,后跟简短的描述。
  • “帮助:<url>”链接到该规则的 axe 文档,包括严重性、补救技巧以及好坏代码示例。
  • “元素:”指向 DOM 中发生失败的位置(使用 HTML 语法)。在此示例中,问题来自具有 aria-hidden="true" 属性和嵌套的 <button> 标记的 span。如果选择器过于复杂而无法找到问题的根源,请使用前面提到的浏览器插件来定位它。如果您大致知道问题出在哪里,也可以尝试向页面添加唯一的 ID 以缩小位置范围。
  • 堆栈跟踪指向 axe 的内部。堆栈跟踪的存在是为了防止测试失败是 axe 中的错误而不是您的代码中的错误,尽管这种情况不太可能发生。

包测试

编辑

打包测试使用 Vagrant 虚拟机作为主机,并使用 Ansible 进行配置和断言。Kibana 发行版从目标文件夹复制到每个 VM 中并安装,同时安装所需的依赖项。

设置

编辑
  • Ansible

    # Ubuntu
    sudo apt-get install python3-pip libarchive-tools
    pip3 install --user ansible
    
    # Darwin
    brew install python3
    pip3 install --user ansible
  • Vagrant
  • Virtualbox

机器

编辑
主机名 IP 描述

deb

192.168.56.5

安装 Kibana 的 deb 包

rpm

192.168.56.6

安装 Kibana 的 rpm 包

docker

192.168.56.7

安装 Kibana 的 docker 镜像

运行

编辑
# Build distributions
node scripts/build --all-platforms --debug

cd test/package

# Setup virtual machine and networking
vagrant up <hostname> --no-provision

# Install Kibana and run OS level tests
# This step can be repeated when adding new tests, it ensures machine state - installations won't run twice
vagrant provision <hostname>

# Running functional tests
node scripts/es snapshot \
  -E network.bind_host=127.0.0.1,192.168.56.1 \
  -E discovery.type=single-node \
  --license=trial
TEST_KIBANA_URL=http://elastic:changeme@<ip>:5601 \
TEST_ES_URL=http://elastic:[email protected]:9200 \
  node scripts/functional_test_runner.js --include-tag=smoke

清理

编辑
vagrant destroy <hostname>

跨浏览器兼容性

编辑

在 OS X 上测试 IE

注意:从 7.9 版本开始不支持 IE11。

  • 下载 VMWare Fusion.
  • 下载适用于 VMWare 的 IE 虚拟机
  • 打开 VMWare 并转到“窗口”>“虚拟机库”。解压缩虚拟机并将 .vmx 文件拖到您的虚拟机库中。
  • 右键单击您刚添加到库中的虚拟机,然后选择“快照…”,然后单击打开的模式窗口中的“拍摄”按钮。当 VM 在 90 天后过期时,您可以回滚到此快照。
  • 在“系统偏好设置”>“共享”中,将您的计算机名称更改为简单的名称,例如“computer”。
  • 使用 yarn start --host=computer.local 运行 Kibana (将 computer.local 替换为你的计算机名称)。
  • 现在你可以运行你的虚拟机,打开浏览器,并导航到 http://computer.local:5601 来测试 Kibana。
  • 或者你可以使用 browserstack。