编写合成测试
在设置 Synthetics 项目之后,您可以开始编写合成测试,用于检查最终用户可能在您的网站上执行的关键操作和请求。
要为您的应用程序编写合成测试,您需要了解基本的 JavaScript 和 Playwright 语法。
Playwright 是由微软开发的一个浏览器测试库。它速度快、可靠,并具有现代化的 API,可自动等待页面元素就绪。
synthetics 代理公开了一个用于创建和运行测试的 API,包括
journey- 测试一个独立的功能单元。接受两个参数:一个
name(字符串)和一个callback(函数)。在创建旅程中了解更多信息。 step- 旅程中应按特定顺序完成的操作。接受两个参数:一个
name(字符串)和一个callback(函数)。在添加步骤中了解更多信息。 expect- 检查某个值是否满足特定条件。支持多种检查。在进行断言中了解更多信息。
beforeAll- 在任何
journey运行之前,运行一次提供的函数。如果提供的函数是一个 promise,运行器将等待 promise 解析后再调用journey。接受一个参数:一个callback(函数)。在设置和移除全局状态中了解更多信息。 之前- 在单个
journey运行之前运行提供的函数。接受一个参数:一个callback(函数)。在设置和移除全局状态中了解更多信息。 afterAll- 在所有
journey运行完成后,运行一次提供的函数。接受一个参数:一个callback(函数)。在设置和移除全局状态中了解更多信息。 之后- 在单个
journey完成后运行提供的函数。接受一个参数:一个callback(函数)。在设置和移除全局状态中了解更多信息。 监控monitor.use方法允许您按旅程逐个确定监视器的配置。例如,如果您希望两个旅程创建具有不同间隔的监视器,则应在每个旅程中调用monitor.use并将schedule属性设置为不同的值。请注意,这仅在使用push命令在 Kibana 或 Observability Serverless 项目中创建监视器时相关。在配置单个监视器中了解更多信息。
使用 .journey.ts 或 .journey.js 文件扩展名创建一个新文件,或者编辑其中一个示例旅程文件。
一个 journey(旅程)测试一个独立的功能单元。例如,登录网站、向购物车添加商品或加入邮件列表。
journey 函数接受两个参数:一个 name 和一个 callback。name 帮助您识别单个旅程。callback 参数是一个封装了旅程操作的函数。该回调提供了对全新 Playwright page、params、browser 和 context 实例的访问权限。
journey('Journey name', ({ page, browser, context, params, request }) => {
// Add steps here
});
name(string)- 用于描述旅程的用户定义字符串。
callback(function)-
您将在其中添加步骤的函数。
实例:
page- 来自 Playwright 的一个 page 对象,允许您控制浏览器的当前页面。
browser- 由 Playwright 创建的一个 browser 对象。
context- 不与其他浏览器上下文共享 cookie 或缓存的一个 browser context(浏览器上下文)。
params- 允许您使用自定义参数调用 Synthetics 套件的用户定义变量。例如,如果您想根据
env使用不同的主页(dev环境使用localhost,prod环境使用某个 URL)。有关更多信息,请参阅使用参数和密钥。 request- 一个请求对象,可用于独立于浏览器交互来发起 API 请求。例如,获取用于基于浏览器的测试的身份验证凭据或令牌。有关更多信息,请参阅发起 API 请求。
一个旅程由一个或多个 steps(步骤)组成。步骤是应按特定顺序完成的操作。步骤会连同屏幕截图一起在 Synthetics UI 中单独显示,以便于调试和错误跟踪。
一个基本的两步旅程如下所示
journey('Journey name', ({ page, browser, client, params, request }) => {
step('Step 1 name', async () => {
// Do something here
});
step('Step 2 name', async () => {
// Do something else here
});
});
步骤可以根据您的需要简单或复杂。例如,基本的第一步可能会加载网页
step('Load the demo page', async () => {
await page.goto('https://elastic.github.io/synthetics-demo/');
});
- 转到
page.goto参考文档了解更多信息。
name(string) |
用于描述旅程的用户定义字符串。 |
callback(function) |
使用 Synthetics 和 Playwright 语法模拟用户工作流的函数。 |
如果您想通过直接与网页交互来生成代码,可以使用 Synthetics Recorder(Synthetics 录制器)。
录制器会启动一个 Chromium 浏览器,该浏览器将监听您与网页的每次交互,并使用 Playwright 在内部进行记录。与浏览器交互完成后,录制器会将记录的操作转换为可用于 Elastic Synthetics 或 Heartbeat 的 JavaScript 代码。
有关开始使用 Synthetics 录制器的更多详细信息,请参阅使用 Synthetics 录制器。
在每个步骤的回调中,您可能会使用大量的 Playwright 语法。使用 Playwright 模拟和验证用户工作流,包括
- 与浏览器或当前页面进行交互(如上例所示)。
- 使用定位器 (locators) 在网页上查找元素。
- 模拟鼠标、触摸或键盘事件。
- 使用
@playwright/test的expect函数进行断言。在进行断言中阅读更多信息。
访问 Playwright 文档获取相关信息。
通过 Elastic 的全球托管测试基础设施或私有位置 (Private Locations) 运行测试时,请勿尝试以有头模式(使用 headless:false)运行,因为这不受支持。
但是,并非所有的 Playwright 功能都应与 Elastic Synthetics 一起使用。在某些情况下,Elastic Synthetics 库中内置了 Playwright 功能的替代方案。这些替代方案旨在更好地用于合成监控。请切勿使用 Playwright 语法来
- 发起 API 请求。请改用 Elastic Synthetics 的
request参数。在发起 API 请求中阅读更多信息。
此外,Elastic Synthetics 中开箱即不支持某些 Playwright 功能,包括
通过 screenshot 或 video 以编程方式进行的捕获不会被存储,也不会显示在 Synthetics 应用程序中。提供 path 可能会因缺少写入本地文件的权限而导致监视器失败。
一个更复杂的 step 可能会等待选择某个页面元素,然后确保它与预期值匹配。
Elastic Synthetics 使用 @playwright/test 的 expect 函数来执行断言,并支持大多数 Playwright 断言。Elastic Synthetics 不支持 toHaveScreenshot 或任何快照断言 (Snapshot Assertions)。
例如,在包含以下 HTML 的页面上
<header class="header">
<h1>todos</h1>
<input class="new-todo"
autofocus autocomplete="off"
placeholder="What needs to be done?">
</header>
您可以使用以下测试来验证带有 new-todo 类的 input 元素是否具有预期的 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-todo类的input元素。 - 使用 Synthetics 代理提供的断言库来检查
placeholder属性的值是否与特定字符串匹配。
您可以使用 request 参数独立于浏览器交互发起 API 请求。例如,您可以从 HTTP 端点检索令牌并在随后的网页请求中使用它。
step('make an API request', async () => {
const response = await request.get(params.url);
// Do something with the response
})
Elastic Synthetics 的 request parameter 类似于 Playwright 公开的其他请求对象,但有一些关键区别
- Elastic Synthetics 的
request参数内置于库中,因此无需单独导入,这减少了所需的代码量,并允许您在内联旅程 (inline journeys) 中发起 API 请求。 - 与 Playwright 的
context.request和page.request(它们与相应的BrowserContext共享 cookie 存储)不同,Elastic Synthetics 公开的顶层request对象拥有自己独立的 cookie 存储。 - 如果您想控制
request对象的创建,可以通过--playwright-options或在synthetics.config.ts文件中传递选项来实现。
有关如何使用 request 对象的完整示例,请参考 Elastic Synthetics 演示仓库。
request 参数并非用于编写纯 API 测试。相反,它是一种在基于浏览器的测试中支持编写普通 HTTP 请求的方法。
如果有任何需要在旅程之前或之后完成的操作,您可以使用 before、beforeAll、after 或 afterAll。
例如,要设置将用于单个 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 包 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 KB。
- 由于平台不一致,原生 Node 模块将无法按预期工作。
基本合成测试的完整示例可能如下所示
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 项目中的所有旅程,请导航到包含 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://esdocs.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)