k6 v1.2.1 版本深度解析:自动扩展解析、gRPC 增强与浏览器模块大更新
【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6
k6 v1.2.1 是 Grafana k6 项目(一款基于 Go 与 JavaScript 的现代化负载测试工具)的一个重要补丁版本,其核心价值在于:将"自动扩展解析"(Automatic Extension Resolution,即此前的 Binary Provisioning)功能面向全体用户默认启用,大幅降低了自定义扩展的使用门槛;同时为 gRPC 模块补齐了特殊浮点值处理与健康检查能力,并为浏览器模块带来了page.route、全套page.getBy*定位 API、locator.all()与page.waitForURL等大量新方法。阅读本文后,你将掌握如何在脚本中直接 import 第三方扩展、如何用一行代码完成 gRPC 服务健康检查,以及如何用语义化定位器与请求拦截写出更稳健的浏览器端测试。
说明:本文所有实现细节均来自当前仓库源码,文中引用的文件均以仓库根目录为起点的相对路径给出,可点击跳转至对应源码深入阅读。
版本发布背景
k6 v1.2.1 本身是一个特殊的补丁版本。据 v1.2.1 发布说明 记载:在合并 xk6-browser 仓库时,旧的 xk6-browser 仓库曾错误地推送了一个 v1.2.0 标签,该标签因残留在合并机器的环境中而无法作为 Go module 使用,也无法通过go install安装。因此官方发布 v1.2.1 以规避该问题——这也是本版本号出现"跳跃"的直接原因。
除版本号问题外,v1.2.1 还携带了三组核心变更:
- 自动扩展解析(Automatic Extension Resolution):由默认关闭的试验性功能转为对所有人默认启用;
- gRPC 模块增强:
NaN/Infinity浮点值的序列化方式修正,以及新增client.healthCheck()方法; - 浏览器模块大更新:
page.route、page.getBy*系列定位器、locator.all()与page.waitForURL。
Breaking Changes:试验性模块的破坏性变更
按照项目的稳定性保证(stability guarantees)约定,跨 minor 版本的破坏性变更仅允许出现在试验性(experimental)功能中。v1.2.1 中的唯一一处 breaking change 正是如此:
实验性的 OpenTelemetry 与 Prometheus 输出(output)现在默认使用 TLSv1.3。这一改动本应是默认行为,实际影响极小——除了让指标输出通道的传输更加安全外,官方明确表示预期不会影响任何现有用户。仓库中与这两类输出对应的实现位于 internal/output/opentelemetry 与 internal/output/prometheusrw,感兴趣可结合源码查看其默认 TLS 配置。
新特性一:自动扩展解析(Automatic Extension Resolution)
从手动编译到自动探测
k6 扩展(extension)允许你为测试脚本增加自定义能力,例如连接数据库、接入消息队列或使用专用网络协议。在 v1.2.1 之前,使用扩展必须手动构建一个将扩展编译进去的自定义 k6 二进制——这要求使用者具备 Go 工具链与构建流程知识,门槛较高。
v1.2.1 引入的自动扩展解析(此前名为 Binary Provisioning)改变了这一切:它默认启用,会自动检测你的脚本是否 import 了扩展,并替你处理"装配正确 k6 二进制"的全部复杂流程。从源码结构看,这一机制与 k6deps(扩展依赖解析器)协同工作,相关实现与测试可参见 internal/js/modules/k6、internal/cmd 下的运行入口逻辑,以及发布说明中提到的 "Passes k6's file system to k6deps"(PR #4958)与 "Adds a test for running tests with the automatic extension resolution disabled"(PR #5014)等内部改动——后者说明项目专门为"禁用该功能"的场景补了回归测试。
使用方式
现在你可以在脚本中直接 import 扩展,无需任何额外构建步骤:
import faker from "k6/x/faker"; export default function () { console.log(faker.person.firstName()); }官方扩展与社区扩展
- 官方扩展:v1.2.1 之前的试验版本只支持官方扩展;
- 社区扩展:PR #4922 新增了对社区扩展列表中任意扩展的支持,只需设置环境变量
K6_ENABLE_COMMUNITY_EXTENSIONS:
K6_ENABLE_COMMUNITY_EXTENSIONS=true k6 run script.js重要限制:社区扩展仅支持本地测试执行(k6 run或k6 cloud run --local-execution)。在 Grafana Cloud k6 上运行测试时,只允许使用官方扩展。
新特性二:gRPC 模块的浮点值处理与健康检查
NaN 与 Infinity 的正确序列化
此前,gRPC 调用返回结果中的NaN或Infinity浮点值会被序列化为null,这与其他 gRPC API 的行为不一致。v1.2.1 起改为使用它们的字符串表示。脚本无需任何改动即可受益。
从源码看,这一逻辑实现在 internal/js/modules/k6/grpc/client.go 中:当转换对象或数组中的浮点值时,代码通过math.IsNaN与math.IsInf分别判断,并映射为"NaN"、"Infinity"与"-Infinity"字符串,从而保证特殊浮点值不再丢失语义。
client.healthCheck():一行代码完成服务健康检查
v1.2.1 为 k6 gRPC 模块新增了client.healthCheck()方法(对应 PR #4853),消除了手写invoke样板代码的需要,尤其适合就绪检查(readiness checks)与服务发现场景。
以前,你需要手写样板代码调用标准健康检查协议:
import grpc from 'k6/grpc'; const client = new grpc.Client(); // ... const response = client.invoke('grpc.health.v1.Health/Check', { service: 'my-service' });现在,一行即可完成:
import grpc from 'k6/grpc'; const client = new grpc.Client(); client.connect('grpc.test.k6.io:443'); // 检查某个具体服务的健康状态 const response = client.healthCheck('my-service'); // 检查整个 gRPC 服务器的健康状态 const overallResponse = client.healthCheck(); client.close();其中不带参数调用healthCheck()会检查整个 gRPC 服务器;传入服务名字符串则检查指定服务的健康状态。方法签名HealthCheck(svc *string)与conn.HealthCheck(...)的底层调用链可在 internal/js/modules/k6/grpc/client.go 中看到。
附带增强:authority 伪头
在 UX 改进列表中,gRPC 模块还新增了对authority伪头的支持(PR #4898),方便需要自定义 authority 的场景。
新特性三:Assertions 断言库(Preview)
k6 现在提供一个 assertions 库,帮助你在测试过程中验证应用行为是否符合预期。它引入了expect()函数与一组富有表现力的匹配器:把值传给expect(),再链式调用定义预期结果的匹配器即可。该库同时适用于协议测试(HTTP/API)与浏览器测试场景,其 API 灵感来自 Playwright 的断言语法,提供更可读、更可靠的流畅式接口。
import { expect } from 'https://jslib.k6.io/k6-testing/0.5.0/index.js'; import { browser } from 'k6/browser'; import http from 'k6/http'; export function protocolTest() { // 获取 k6 Quick Pizza 应用首页 const response = http.get('https://quickpizza.grafana.com/'); // 简单断言 expect(response.status).toBe(200); expect(response.error).toEqual(''); expect(response.body).toBeDefined(); } export async function browserTest() { const page = await browser.newPage(); try { await page.goto('https://quickpizza.grafana.com/'); // 断言 "Pizza Please" 按钮可见 await expect(page.locator('button[name=pizza-please]')).toBeVisible(); } finally { await page.close(); } } export const options = { scenarios: { // 协议测试 protocol: { executor: 'shared-iterations', vus: 1, iterations: 1, exec: 'protocolTest', }, // 浏览器测试 ui: { executor: 'shared-iterations', options: { browser: { type: 'chromium', }, }, exec: 'browserTest', }, }, };预览版声明
该功能已可实际使用,但仍处于预览状态:
- 既不计划、也不预期会有破坏性变更;
- 部分功能可能缺失或尚不完善;
- 官方会持续增加匹配器并改进覆盖率。
官方欢迎反馈与贡献(反馈渠道指向 k6-jslib-testing 仓库)。
新特性四:浏览器模块的定位器与请求拦截
语义化定位:全套 page.getBy* API
v1.2.1 为浏览器模块补齐了基于语义与可访问性属性的定位方法,全部支持字符串与正则表达式两种形式。相比 CSS 类名或 ID,这些选择器在 UI 重构时更不易变动,测试更稳健。
page.getByRole:按 ARIA 角色定位
ARIA 角色(button、link、textbox 等)代表元素的用途或功能,是优秀的测试选择器。对应 PR #4843:
// 按角色查找元素 await page.getByRole('button').click(); // 按角色 + 可访问名称查找 await page.getByRole('button', { name: 'Submit' }).click(); // name 同样支持正则 await page.getByRole('textbox', { name: /^Username$/ }).fill('admin'); // 配合特定状态使用 await page.getByRole('checkbox', { name: 'Accept terms', checked: true }).click(); // 按标题级别查找 await page.getByRole('heading', { level: 2, name: 'Section Title' }).textContent();page.getByAltText:按替代文本定位
用于定位带alt属性的图片等元素(PR #4881)。此前需要 CSS 或 XPath:
// 使用 CSS 选择器 const locator = page.locator('img[alt="World Map"]'); // 使用 XPath 选择器 const locator = page.locator('//img[@alt="World Map"]');现在:
const locator = page.getByAltText('World Map'); // 查找 alt 文本以 'World' 开头的图片 const locator = page.getByAltText(/^World/);page.getByLabel:按标签文本定位表单元素
同时支持显式<label>元素与aria-label属性(PR #4890)。此前 CSS 难以处理 label 与表单元素的关系,只能使用 XPath:
// 用 XPath 按标签文本查找输入框 const locator = page.locator('//label[text()="Password"]'); // 或用 CSS 匹配 aria-label const locator = page.locator('[aria-label="Username"]');现在:
// 同时兼容 <label> 元素与 aria-label 属性 const passwordInput = page.getByLabel('Password'); // 同样支持正则 const usernameInput = page.getByLabel(/^Username$/);page.getByPlaceholder:按占位文本定位
适合定位 input、textarea 等带 placeholder 的表单控件(PR #4904)。此前:
// 使用 CSS 选择器 const locator = page.locator('input[placeholder="Enter your name"]'); // 使用 XPath 选择器 const locator = page.locator('//input[@placeholder="Enter your name"]');现在:
const nameInput = page.getByPlaceholder('Enter your name'); // 同样支持正则 const emailInput = page.getByPlaceholder(/^Email/);page.getByTitle:按 title 属性定位
适合定位 tooltip、按钮等带title属性的元素(PR #4910)。此前:
// 使用 CSS 选择器 const locator = page.locator('div[title="Information box"]'); // 使用 XPath 选择器 const locator = page.locator('//div[@title="Information box"]');现在:
const infoBox = page.getByTitle('Information box'); // 同样支持正则 const saveButton = page.getByTitle(/^Save/);page.getByTestId:按>// 使用 CSS 选择器 const locator = page.locator('button[data-testid="submit-button"]'); // 使用 XPath 选择器 const locator = page.locator('//button[@data-testid="submit-button"]');
现在:
const submitButton = page.getByTestId('submit-button'); // 同样支持正则 const usernameInput = page.getByTestId(/^username/);page.getByText:按可见文本定位
CSS 无法直接查询元素文本,此前只能用 XPath(PR #4912):
// 使用 XPath 选择器 const locator = page.locator('//div[text()="Hello World"]');现在:
const helloWorldElement = page.getByText('Hello World'); // 同样支持正则 const submitButton = page.getByText(/^Submit/);page.route():拦截与处理网络请求
page.route()允许你在请求发送前拦截并处理它们(PR #4953、#4961、#4971、#4985)。这非常适合模拟 API 响应、屏蔽特定资源或修改请求行为等场景。路由处理器接收一个route对象,提供abort()、continue()与fulfill()三个方法。
屏蔽请求:阻止特定资源(如图片、广告、分析脚本)加载:
// 屏蔽所有图片请求 await page.route(/(\.png$)|(\.jpg$)|(\.jpeg$)/, async (route) => { await route.abort(); });模拟响应:不访问真实端点,直接返回自定义响应:
// 模拟 API 响应 await page.route('**/api/users', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([{ id: 1, name: 'Mock User' }]) }); });修改请求:在请求发出前修改请求头、URL 或请求体:
// 以修改后的请求头继续 await page.route('**/api/**', async (route) => { await route.continue({ headers: { ...route.request().headers(), 'Authorization': 'Bearer mock-token' } }); });locator.all():批量获取匹配元素
locator.all()返回匹配选择器的全部元素的 locator 数组(PR #4899),适合处理列表项、同款式按钮等页面上的多个相似元素:
// 获取所有列表项并遍历 const items = await page.locator('li').all(); for (const item of items) { console.log(await item.textContent()); }page.waitForURL / frame.waitForURL:等待导航到指定 URL
waitForURL同时加入page与frame对象(PR #4917、#4920)。作为前置条件,waitForNavigation现在也接受url选项,但官方建议优先使用waitForURL。
waitForURL的处理逻辑是:先检查当前页面 URL 是否已匹配预期模式——若已匹配,则等待 load 状态完成;否则等待导航到指定 URL。这种方式可以避免"页面在等待条件建立之前就完成导航"的竞态条件,尤其适合包含多次重定向的页面。它同时支持字符串模式与正则表达式:
// 等待导航到特定 URL await Promise.all([ page.waitForURL('https://quickpizza.grafana.com/my_messages.php'), page.locator('a[href="/my_messages.php"]').click(), ]); // 使用正则模式 await Promise.all([ page.waitForURL(/.*\/contacts\.php.*/), page.locator('a[href^="/contacts.php"]').click() ]);尽管waitForURL提供了便捷的 URL 等待方式,官方仍建议优先采用基于元素的等待策略或带有内置自动等待能力的 locator API,以获得更可靠的测试。
UX 改进与增强
- #4878:测试结束摘要中无检查项(checks)时不再报告
NaN百分比; - #4897:浏览器模块
locator.selectOption支持字符串标签; - #4898:gRPC 模块支持
authority伪头; - #4916:错误输出更一致、更准确;
- #4918:浏览器模块从导航事件中暴露导航错误;
- #4919:
page.url()不再调用浏览器,改用缓存版本,速度显著提升且与 Playwright 保持一致; - #4932:浏览器模块中明确提示请求被中止;
- #4944:新增 Prometheus 指标端点;
- #5040 中的模板文件);
- #4976:测试结束摘要中的指标对齐方式调整,减少点号与横向空间占用。
Bug 修复
- #4850:修复浏览器模块中整数类型之间的错误转换;
- #4973:修复浏览器模块中在创建
Page之前请求BrowserContext导致的 panic; - #4975:修复浏览器模块中的潜在竞态条件;
- #5015:修复
waitForNavigation在未调用page.close时阻塞迭代结束的问题; - #5017:修复 gRPC 模块从远程服务器获取 protobuf 定义时的潜在竞态条件。
维护与内部改进
- #4666:降低部分过于嘈杂的 cloud 输出日志级别;
- #4879、#4945:更新打包 k6 的内部 k6packager 所用软件;
- #4886:防止公共 fork 触发 Winsign,避免 CI 失败与资源浪费;
- #4892 等 14 个 PR:更新依赖;
- #4925:配置 Dependabot 更新所有 OTel 依赖;
- #4914 等 8 个 PR:提升 CI 测试稳定性;
- #4923:在 README 中提及 k6 studio;
- #4931:降低内部实现细节错误的 debug 日志级别;
- #4943、#4946、#4948:修复 CI 未检查生成文件及版本固定问题;
- #4949);
- #4950:新增 Dependabot 规则更新主 Dockerfile 依赖;
- #4958:将 k6 的文件系统传递给 k6deps(服务于自动扩展解析);
- #4959:复制 Zizmor 配置到仓库以支持 fork 使用;
- #4960:降低
waitForNavigation的复杂度; - #4969、#4986:更新 semgrep 安全扫描;
- #4994:配置 Dependabot 更新 GitHub Actions 依赖;
- #4996、#4997、#4998:更新 GitHub Actions 依赖;
- #5014:新增"禁用自动扩展解析运行测试"的测试;
- #5016:修复自动扩展解析启用状态下的 internal/cmd/tests;
- #5020:修复示例中浏览器脚本使用错误端点的问题。
总结
k6 v1.2.1 是一个承前启后的版本:自动扩展解析的默认启用让 k6 扩展体系从"开发者构建"走向"脚本即所得";gRPC 模块在特殊浮点值与健康检查上的完善,让协议级测试脚本更简洁、更符合业界惯例;浏览器模块在语义化定位与请求拦截上的集中补强,则让端到端测试的编写体验向 Playwright 等现代框架看齐。如果你正在使用 k6 编写负载或端到端测试,本版本值得立即升级体验。
相关发布说明原文可参阅 release notes/v1.2.1.md,gRPC 模块实现可深入 internal/js/modules/k6/grpc/client.go 继续研究。
【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考