测试编辑

运行特定的 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,详细了解如何使用我们提供的节点脚本构建 Kibana、运行集成测试以及在您开发时启动 Kibana 和 Elasticsearch。

更多测试信息:编辑

功能测试编辑

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

运行功能测试编辑

FunctionalTestRunner (FTR) 非常精简,它的大部分功能都来自其配置文件。Kibana 存储库包含许多 FTR 配置文件,这些文件对 Kibana 服务器或 Elasticsearch 使用略有不同的配置,具有不同的测试文件,以及其他潜在的配置差异。您可以在 .buildkite/ftr_configs.yml 中找到所有 FTR 配置文件的清单。如果您正在 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
    • 在针对云部署运行时,某些测试不适用。要跳过不适用的测试,请使用 --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 启动 Elasticsearch 和 Kibana 服务器,并将 WebDriver 测试配置为在 Chrome 中为特定应用程序运行。例如,--config test/functional/apps/home/config.js 启动 Elasticsearch 和 Kibana 服务器,并将 WebDriver 测试配置为在 Chrome 中为 home 应用程序运行。
  • --config test/functional/config.firefox.js 启动 Elasticsearch 和 Kibana 服务器,并将 WebDriver 测试配置为在 Firefox 中运行。
  • --config test/api_integration/config.js 启动 Elasticsearch 和 Kibana 服务器,并使用 api 集成测试配置。
  • --config test/accessibility/config.ts 启动 Elasticsearch 和 Kibana 服务器,并将 WebDriver 测试配置为使用 axe 运行辅助功能审计。

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

配置提供程序

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

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

  • ciGroup{id} - 将测试套件分配给特定的 CI 工作程序
  • skipCloudskipFirefox - 从 Cloud 或 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 的新服务(请参阅 test/common/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() 函数的提供程序 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(...
  });

}
提供程序 API编辑

所有提供程序的第一个也是唯一一个参数是提供程序 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 服务
    • 有关完整的 API,请参阅 selenium-webdriver 文档
自定义服务编辑

服务是有意设计为通用的。它们可以是任何东西(甚至什么都不是)。有些服务有帮助程序,用于与特定类型的 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
        }
      }
    }
页面对象编辑

每个页面对象的用途都非常不言自明。可视化页面对象提供了与可视化应用程序交互的帮助程序,仪表板页面对象提供了与仪表板应用程序交互的帮助程序,等等。

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

请将新方法添加到现有服务或新服务中,而不是进一步扩展 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 设置…”* 取消选中“优先使用集成 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)
  • “仪表板”和“创建仪表板按钮”是失败的测试套件和特定测试的名称。
  • 始终在括号中,“[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 文件拖到您的虚拟机库中。
  • 右键单击您刚刚添加到库中的虚拟机并选择“快照…”,然后单击打开的模式中的“拍摄”按钮。当虚拟机在 90 天后过期时,您可以回滚到此快照。
  • 在系统偏好设置 > 共享中,将您的计算机名称更改为简单的名称,例如“computer”。
  • 使用 yarn start --host=computer.local 运行 Kibana(替换您的计算机名称)。
  • 现在您可以运行您的虚拟机,打开浏览器,然后导航到 http://computer.local:5601 来测试 Kibana。
  • 或者,您可以使用 browserstack