菜单
开源

模拟用户输入延迟

在本页面上,您将了解如何在 k6 中最好地使用 sleep,以及在 k6/browser 中可用的各种 wait* 前缀方法,以模拟用户输入延迟、等待导航以及等待元素状态变化。阅读完本页后,您应该能够在必要时成功使用正确的 API。

注意

虽然使用 sleeppage.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

JavaScript
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 元素和变量的变化。

JavaScript
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 会很有用。这主要用于处理单页应用或未发生完整页面重新加载的情况。

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();

  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 中调用非常重要。

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();

  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 执行而延迟加载。

js
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 相同,但它是异步的,这意味着它不会阻塞事件循环,并允许后台任务继续执行。

js
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();
  }
}