编写合成测试

编辑

设置项目后,您可以开始编写合成测试,以检查最终用户可能在您的网站上执行的关键操作和请求。

语法概述
编辑

要为您的应用程序编写合成测试,您需要了解基本的 JavaScript 和Playwright语法。

Playwright是微软开发的一个浏览器测试库。它速度快、可靠,并具有现代 API,可以自动等待页面元素准备就绪。

合成代理公开了用于创建和运行测试的 API,包括

journey

测试一个独立的功能单元。接受两个参数:name(字符串)和callback(函数)。在创建旅程中了解更多信息。

step

旅程中的操作,应按特定顺序完成。接受两个参数:name(字符串)和callback(函数)。在添加步骤中了解更多信息。

expect

检查某个值是否满足特定条件。有多个支持的检查。在进行断言中了解更多信息。

beforeAll

在任何journey运行之前,只运行一次提供的函数。如果提供的函数是 Promise,则运行器将在调用journey之前等待 Promise 解析。接受一个参数:callback(函数)。在设置和移除全局状态中了解更多信息。

before

在单个journey运行之前运行提供的函数。接受一个参数:callback(函数)。在设置和移除全局状态中了解更多信息。

afterAll

在所有journey运行完成后,只运行一次提供的函数。接受一个参数:callback(函数)。在设置和移除全局状态中了解更多信息。

after

在单个journey完成后运行提供的函数。接受一个参数:callback(函数)。在设置和移除全局状态中了解更多信息。

monitor

monitor.use方法允许您逐个旅程确定监控的配置。例如,如果希望两个旅程创建具有不同间隔的监控,则应在每个旅程中调用monitor.use,并在每个旅程中将schedule属性设置为不同的值。请注意,这仅在使用push命令在 Kibana 中创建监控时才相关。在配置单个监控中了解更多信息。

创建旅程
编辑

使用.journey.ts.journey.js文件扩展名创建新文件,或编辑示例旅程文件之一。

旅程测试一个独立的功能单元。例如,登录网站、将商品添加到购物车或加入邮件列表。

journey 函数接受两个参数:namecallbackname帮助您识别单个旅程。callback参数是一个封装旅程功能的函数。回调提供了对新的 Playwright pageparamsbrowsercontext实例的访问。

journey('Journey name', ({ page, browser, context, params, request }) => {
  // Add steps here
});
参数
编辑

name (string)

用户定义的字符串,用于描述旅程。

callback (function)

您将在其中添加步骤的函数。

实例:

page
来自 Playwright 的page对象,允许您控制浏览器的当前页面。
browser
Playwright 创建的browser对象。
context
一个浏览器上下文,它不与其他浏览器上下文共享 Cookie 或缓存。
params
用户定义的变量,允许您使用自定义参数调用 Synthetics 套件。例如,如果希望根据env使用不同的主页(localhost用于dev,URL 用于prod)。有关更多信息,请参阅使用参数和密钥
request
一个请求对象,可用于独立于浏览器交互进行 API 请求。例如,获取身份验证凭据或令牌以服务于基于浏览器的测试。有关更多信息,请参阅发出 API 请求
添加步骤
编辑

一个旅程包含一个或多个步骤。步骤是应按特定顺序完成的操作。步骤在 Synthetics 应用程序中单独显示,并附带屏幕截图,便于调试和错误跟踪。

一个基本的双步骤旅程如下所示

journey('Journey name', ({ page, browser, client, params, request }) => {
    step('Step 1 name', () => {
      // Do something here
    });
    step('Step 2 name', () => {
      // Do something else here
    });
});

步骤可以像您需要的那样简单或复杂。例如,一个基本的第一个步骤可能会加载一个网页

step('Load the demo page', () => {
  await page.goto('https://elastic.github.io/synthetics-demo/'); 
});

有关更多信息,请访问page.goto参考

参数
编辑

name (string)

用户定义的字符串,用于描述旅程。

callback (function)

一个函数,您可以在其中使用 Synthetics 和Playwright语法模拟用户工作流。

如果希望通过直接与网页交互来生成代码,可以使用Synthetics 记录器

记录器会启动一个Chromium 浏览器,该浏览器将侦听您与网页的每次交互,并使用 Playwright 在内部记录它们。完成与浏览器的交互后,记录器会将记录的操作转换为 JavaScript 代码,您可以将其与 Elastic Synthetics 或 Heartbeat 一起使用。

有关 Synthetics 记录器入门详细信息,请参阅使用 Synthetics 记录器

Playwright 语法
编辑

在每个步骤的回调内部,您可能会使用大量 Playwright 语法。使用 Playwright 模拟和验证用户工作流,包括

访问Playwright 文档以获取信息。

在通过 Elastic 的全局托管测试基础设施或私有位置运行时,请勿尝试以无头模式(使用headless:false)运行,因为不支持此功能。

但是,并非所有 Playwright 功能都应与 Elastic Synthetics 一起使用。在某些情况下,Elastic Synthetics 库中内置了 Playwright 功能的替代方案。这些替代方案旨在更好地用于合成监控。请勿使用 Playwright 语法来

  • 发出 API 请求。请改用 Elastic Synthetic 的request参数。在发出 API 请求中阅读更多内容。

Elastic Synthetics 中也有一些 Playwright 功能在开箱即用时不受支持,包括

通过screenshotvideo以编程方式完成的捕获不会被存储,也不会在 Synthetics 应用程序中显示。提供path可能会导致监控失败,因为缺少写入本地文件的权限。

进行断言
编辑

更复杂的step可能会等待页面元素被选中,然后确保它与预期值匹配。

Elastic Synthetics 使用 @playwright/testexpect 函数进行断言,并支持大多数 Playwright 断言。Elastic Synthetics 不支持 toHaveScreenshot 或任何 Snapshot 断言

例如,在一个使用以下 HTML 的页面上

<header class="header">
  <h1>todos</h1>
  <input class="new-todo"
    autofocus autocomplete="off"
    placeholder="What needs to be done?">
</header>

您可以使用以下测试来验证具有类 new-todoinput 元素是否具有预期的 placeholder 值(input 元素的提示文本)

step('Assert placeholder text', async () => {
  const input = await page.locator('input.new-todo'); 
  expect(await input.getAttribute('placeholder')).toBe(
    'What needs to be done?'
  ); 
});

查找具有类 new-todoinput 元素。

使用 Synthetics 代理提供的断言库来检查 placeholder 属性的值是否与特定字符串匹配。

发出 API 请求
编辑

您可以使用 request 参数独立于浏览器交互发出 API 请求。例如,您可以从 HTTP 端点检索令牌并在后续网页请求中使用它。

step('make an API request', async () => {
  const response = await request.get(params.url);
  // Do something with the response
})

Elastic Synthetics 的 request 参数类似于 Playwright 公开的其他请求对象,但有一些关键区别

  • Elastic Synthetics 的 request 参数内置于库中,因此无需单独导入,从而减少了所需的代码量,并允许您在 内联旅程 中发出 API 请求。
  • Elastic Synthetics 公开的顶级 request 对象拥有自己的独立 Cookie 存储,这与 Playwright 的 context.requestpage.request 不同,后者与相应的 BrowserContext 共享 Cookie 存储。
  • 如果您想控制 request 对象的创建,可以通过 --playwright-options 或在 synthetics.config.ts 文件 中传递选项来实现。

有关显示如何使用 request 对象的完整示例,请参阅 Elastic Synthetics 演示存储库

request 参数并非旨在用于编写纯 API 测试。相反,它是一种在基于浏览器的测试中支持编写纯 HTTP 请求的方法。

设置和删除全局状态
编辑

如果在旅程之前或之后需要执行任何操作,可以使用 beforebeforeAllafterafterAll

例如,要设置将在 单个 journey 中使用的全局状态或服务器,请使用 before 钩子。要执行此设置并在 所有 旅程之前执行一次,请使用 beforeAll 钩子。

before(({ params }) => {
  // Actions to take
});

beforeAll(({ params }) => {
  // Actions to take
});

您可以使用 after 钩子清理 单个 journey 使用的全局状态或关闭服务器。要执行此清理并在所有旅程之后执行一次,请使用 afterAll 钩子。

after(({ params }) => {
  // Actions to take
});

afterAll(({ params }) => {
  // Actions to take
});
导入 NPM 包
编辑

您可以在旅程代码中导入和使用其他 NPM 包。请参阅以下使用外部 NPM 包 is-positive 的示例

import { journey, step, monitor, expect } from '@elastic/synthetics';
import isPositive from 'is-positive';

journey('bundle test', ({ page, params }) => {
  step('check if positive', () => {
    expect(isPositive(4)).toBe(true);
  });
});

当您从使用外部 NPM 包的旅程 创建监控 时,这些包将在调用 push 命令时与旅程代码一起捆绑。

但是,使用外部包时存在一些限制

  • 压缩后的捆绑旅程不应超过 800 千字节。
  • 由于平台不一致,原生节点模块将无法按预期工作。
合成测试示例
编辑

基本合成测试的完整示例可能如下所示

import { journey, step, expect } from '@elastic/synthetics';

journey('Ensure placeholder is correct', ({ page }) => {
  step('Load the demo page', async () => {
    await page.goto('https://elastic.github.io/synthetics-demo/');
  });
  step('Assert placeholder text', async () => {
    const placeholderValue = await page.getAttribute(
      'input.new-todo',
      'placeholder'
    );
    expect(placeholderValue).toBe('What needs to be done?');
  });
});

您可以在 Elastic Synthetics 演示存储库 中找到更复杂的示例。

本地测试
编辑

在编写旅程时,您可以将其在本地运行以验证其是否按预期工作。然后,您可以创建监控以定期运行您的旅程。

要测试项目中的所有旅程,请导航到包含 Synthetics 项目的目录并在其中运行旅程。默认情况下,@elastic/synthetics 运行程序只会运行与文件名 *.journey.(ts|js)* 匹配的文件。

# Run tests on the current directory. The dot `.` indicates
# that it should run all tests in the current directory.
npx @elastic/synthetics .
测试内联监控
编辑

要在本地测试内联监控的旅程,请将内联旅程管道到 npx @elastic/synthetics 命令中。

例如,假设您的内联监控包含以下代码

step('load homepage', async () => {
    await page.goto('https://elastic.ac.cn');
});
step('hover over products menu', async () => {
    await page.hover('css=[data-nav-item=products]');
});

要本地运行该旅程,您可以将该代码保存到文件中,并将文件的内容管道到 @elastic-synthetics

cat path/to/sample.js | npx @elastic/synthetics --inline

您将获得如下响应

Journey: inline
   ✓  Step: 'load homepage' succeeded (1831 ms)
   ✓  Step: 'hover over products menu' succeeded (97 ms)

 2 passed (2511 ms)