sodcalculator

10 Tech How-To

k6 Browser Testing: A Step-by-Step Guide

Guide 10-31171 · Updated · Step by step

This guide walks you through using k6’s browser testing capabilities to automate and load-test web applications in a real Chromium browser. You’ll learn how to set up the environment, write a browser test script, run it, and interpret the results, including how to handle common pitfalls.

What You’ll Need

  • k6 installed – Download and install the latest k6 binary from the official website or via your package manager (e.g., brew install k6 on macOS, choco install k6 on Windows).
  • Node.js (optional) – Only if you plan to use the k6 Studio or the experimental browser module with a bundler. For the standard approach, k6’s built-in browser module works without Node.
  • A test website – Use a public site like https://test.k6.io or your own local development server.
  • Basic JavaScript knowledge – k6 scripts are written in JavaScript, so familiarity with async/await and selectors is helpful.

Step-by-Step

  1. Create a new JavaScript file for your test, e.g., browser-test.js. This is where you’ll write the browser automation and load test logic.
  2. Import the browser module at the top of your script:
    import { browser } from 'k6/experimental/browser';

    This module provides a Playwright-like API for controlling Chromium.

  3. Define the test options – Configure the load profile and browser-specific settings. For example:
    export const options = {
      scenarios: {
        ui: {
          executor: 'shared-iterations',
          options: {
            browser: {
              type: 'chromium',
            },
          },
          exec: 'browserTest',
          vus: 1,
          iterations: 1,
        },
      },
    };

    Here, vus and iterations control the number of virtual users and how many times each runs. For browser testing, start with 1 VU and 1 iteration to validate the script.

  4. Write the browser test function – Create a function (e.g., browserTest) that uses the browser object to open a page, interact with elements, and assert behavior. A minimal example:
    export async function browserTest() {
      const page = await browser.newPage();
      try {
        await page.goto('https://test.k6.io');
        await page.waitForSelector('h1');
        console.log('Page title:', await page.title());
      } finally {
        await page.close();
      }
    }

    The try/finally ensures the page is closed even if an assertion fails.

  5. Add interactions and assertions – Use Playwright-style methods like page.click(), page.fill(), and page.waitForNavigation(). For example, to fill a form and submit:
    await page.fill('#username', 'testuser');
    await page.fill('#password', 'secret');
    await Promise.all([
      page.waitForNavigation(),
      page.click('button[type="submit"]'),
    ]);

    Always wait for navigation after a click that triggers a page load.

  6. Run the test – Execute the script with the command:
    k6 run browser-test.js

    k6 will launch a headless Chromium browser, run your script, and output metrics to the terminal.

  7. View the results – After the run, k6 prints summary metrics like browser_http_req_duration (request latency) and browser_web_vital_fcp (First Contentful Paint). These help you assess performance. For a detailed report, you can output to JSON or use k6 Cloud.

Common Mistakes

  • Not closing pages – Leaving pages open can cause memory leaks and affect test results. Always use try/finally or page.close().
  • Using incorrect selectors – Overly brittle selectors (e.g., CSS paths with dynamic IDs) break easily. Prefer stable attributes like data-testid or text-based selectors.
  • Ignoring wait conditions – Failing to wait for elements to appear before interacting leads to flaky tests. Use waitForSelector() or waitForNavigation().
  • Setting too many VUs – Running many concurrent browser instances consumes significant CPU and memory. Start with 1–2 VUs, then scale up gradually.
  • Mixing browser and non-browser code – The browser module cannot be used in the same iteration as regular HTTP requests without careful setup. Keep browser scenarios separate.

Tips

  • Use the browser recorder – k6 Studio (formerly k6 Recorder) can generate a script from your interactions in a real browser. This saves time and provides a solid starting point.
  • Leverage environment variables – For credentials or URLs, use __ENV to avoid hardcoding sensitive data: const url = __ENV.BASE_URL || 'https://test.k6.io';
  • Add thresholds – Define pass/fail criteria in options.thresholds, e.g., thresholds: { 'browser_web_vital_fcp': ['p(95) < 2500'] } to fail the test if the 95th percentile FCP exceeds 2.5 seconds.
  • Run locally first – Always test with a single VU to ensure the script is stable before running a full load test.
  • Check the official examples – The k6 documentation has a repository of browser test examples covering common scenarios like login flows and single-page apps.

Troubleshooting

  • Browser not launching – Ensure you have the latest k6 version (browser support is experimental but stable in recent releases). If you're on Linux, you may need to install Chromium dependencies: sudo apt-get install -y chromium-browser or equivalent.
  • Script fails with 'browser is not defined' – This usually means you're trying to use the browser module outside a browser scenario. Make sure your function is referenced in the exec field of a scenario with type: 'chromium'.
  • Timeout errors on page.goto() – Increase the default timeout by passing options: await page.goto(url, { timeout: 60000 }). Also check network connectivity and that the site is accessible.
  • Element not found even though it exists – The element may be inside an iframe or shadow DOM. Use page.frames() to switch to the correct frame, or use page.$(selector) with a shadow-piercing selector if supported.
  • Memory usage spikes – Close pages after each iteration and consider reducing the number of VUs. Also, avoid taking screenshots in every iteration unless needed.

Frequently Asked Questions

Do I need to install a separate browser?

No, k6 with the browser module downloads and manages its own Chromium instance automatically. You don't need to install Chrome or Chromium separately.

Can I use k6 browser testing for load testing with many virtual users?

Yes, but each VU runs a full browser instance, which is resource-intensive. For realistic load testing, it's common to use a few browser VUs for critical user journeys and supplement with protocol-level VUs (HTTP requests) for scale.

How do I take a screenshot or record video?

Use page.screenshot({ path: 'screenshot.png' }) for a screenshot. For video, you need to set recordVideo: { dir: 'videos' } in the browser scenario options.

What metrics does k6 browser testing provide?

It provides browser-specific metrics like browser_http_req_duration (HTTP request latency), browser_web_vital_fcp (First Contentful Paint), browser_web_vital_lcp (Largest Contentful Paint), and browser_web_vital_cls (Cumulative Layout Shift).

Can I use k6 browser testing for single-page applications (SPAs)?

Yes, the browser module supports client-side routing. Use page.waitForNavigation() or page.waitForSelector() after clicking links or buttons that change the view without a full page reload.

Is the browser module stable for production use?

As of the latest k6 releases, the browser module is considered stable enough for most testing scenarios, but it's still marked as 'experimental' in the import path. Always test on your target site and keep k6 updated.

Related

General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.

More in this bed

The whole Tech How-To bed →