模拟用户输入延迟
在本页面上,您将了解如何在 k6
中最好地使用 sleep
,以及在 k6/browser
中可用的各种 wait*
前缀方法,以模拟用户输入延迟、等待导航以及等待元素状态变化。阅读完本页后,您应该能够在必要时成功使用正确的 API。
注意
虽然使用
sleep
或page.waitForTimeout
函数等待元素状态变化可能看起来很有帮助,但最好避免使用它们以防止测试不稳定。相反,最好使用本页列出的其他wait*
前缀方法。
什么是 sleep
?
sleep 是 k6 内置的一个一级函数。它的主要用途是“在指定持续时间内暂停 VU 执行”,这在您想要模拟用户输入延迟时最有用,例如:
- 导航到页面。
- 睡眠一秒钟以模拟用户在页面上查找特定元素。
- 点击该元素。
警告
sleep
是一个同步函数,它会阻塞 JavaScript 事件循环,这意味着所有异步工作也会被暂停,直到sleep
完成。浏览器模块主要提供异步 API,因此最好避免使用
sleep
。相反,请使用 page.waitForTimeout 函数。
什么是 wait*
?
在浏览器模块中有各种异步 API 可用于等待某些状态
方法 | 描述 |
---|---|
browserContext.waitForEvent | 等待所选事件触发并返回其值。 |
page.waitForFunction | 等待给定函数返回一个真值。 |
page.waitForLoadState | 等待指定的页面生命周期事件。 |
page.waitForNavigation | 等待导航启动后完成。 |
page.waitForTimeout | 等待指定的时间。在前端测试中请使用此方法代替 sleep 。 |
locator.waitFor | 等待元素达到特定状态。 |
browserContext.waitForEvent
browserContext.waitForEvent 当等待特定事件触发时使用,然后它会返回与该事件关联的元素。目前唯一可用的事件是 page
。当您想要跟踪和检索新打开的标签页时,这会很有用。在使用谓词函数时,它可用于等待特定页面打开,然后返回 true
。
import { browser } from 'k6/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
await page.goto('https://test.k6.io/');
await page.keyboard.down('ControlOrMeta');
// Open the link in a new tab with the help of the meta key.
// Wait for the new page to be created.
const browserContext = browser.context();
const [newTab] = await Promise.all([
browserContext.waitForEvent('page'),
page.locator('a[href="/my_messages.php"]').click(),
]);
await page.keyboard.up('ControlOrMeta');
// Wait for the new page (tab) to load.
await newTab.waitForLoadState('load');
// Take screenshots of each page.
await page.screenshot({ path: `screenshot-page.png` });
await newTab.screenshot({ path: `screenshot-newTab.png` });
await newTab.close();
await page.close();
}
page.waitForFunction
当您想要通过一个 JavaScript 函数更精细地控制测试何时进行,该函数在满足一个或多个条件时返回 true,这时 page.waitForFunction 会很有用。它可用于轮询 DOM 或非 DOM 元素和变量的变化。
import { browser } from 'k6/browser';
import { check } from 'https://jslib.k6.io/k6-utils/1.5.0/index.js';
export const options = {
scenarios: {
browser: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
// Setting up the example that will mutate the h1 element by setting the
// h1 elements text value to "Hello".
await page.evaluate(() => {
setTimeout(() => {
const el = document.createElement('h1');
el.innerHTML = 'Hello';
document.body.appendChild(el);
}, 1000);
});
// Waiting until the h1 element has mutated.
const ok = await page.waitForFunction("document.querySelector('h1')", {
polling: 'mutation',
timeout: 2000,
});
await check(ok, {
'waitForFunction successfully resolved': async (ok) => (await ok.innerHTML()) == 'Hello',
});
} finally {
await page.close();
}
}
page.waitForLoadState
当没有显式导航,但您需要等待页面或网络稳定时,page.waitForLoadState 会很有用。这主要用于处理单页应用或未发生完整页面重新加载的情况。
import { browser } from 'k6/browser';
export const options = {
scenarios: {
browser: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
// Goto a SPA
await page.goto('<url>');
// ... perform some actions that reload part of the page.
// waits for the default `load` event.
await page.waitForLoadState();
} finally {
await page.close();
}
}
page.waitForNavigation
page.waitForNavigation 是一个非常有用的 API,用于在执行可能触发页面导航的其他操作时使用,因为这些操作不会自动等待导航结束。通常,您会在我们的示例中看到它与 click
API 调用一起使用。请注意,goto 是一个不需要 waitForNavigation
的 API 示例,因为它会在返回之前自动等待导航完成。
将此方法与会触发导航启动的 API 一起在 Promise.all 中调用非常重要。
import { browser } from 'k6/browser';
export const options = {
scenarios: {
browser: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://test.k6.io/my_messages.php');
await page.locator('input[name="login"]').type('admin');
await page.locator('input[name="password"]').type('123');
const submitButton = page.locator('input[type="submit"]');
// The click action will start a navigation, and the waitForNavigation
// will help the test wait until the navigation completes.
await Promise.all([page.waitForNavigation(), submitButton.click()]);
} finally {
await page.close();
}
}
locator.waitFor
locator.waitFor 将等待元素满足等待条件。这对于处理动态网站很有用,因为在动态网站中,元素可能需要一些时间才能出现或改变状态。例如,如果元素由于异步调用或缓慢的 JavaScript 执行而延迟加载。
import { browser } from 'k6/browser';
export const options = {
scenarios: {
browser: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
await page.goto('https://test.k6.io/browser.php');
const text = page.locator('#input-text-hidden');
await text.waitFor({
state: 'hidden',
});
}
page.waitForTimeout
page.waitForTimeout 将等待指定的时间量。它的功能与 k6 的 sleep 相同,但它是异步的,这意味着它不会阻塞事件循环,并允许后台任务继续执行。
import { browser } from 'k6/browser';
export const options = {
scenarios: {
browser: {
executor: 'shared-iterations',
options: {
browser: {
type: 'chromium',
},
},
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://test.k6.io');
// Slow the test down to mimic a user looking for the element on the page.
await page.waitForTimeout(1000);
// ... perform the next action
} finally {
await page.close();
}
}