TESTEVERYTHING

Showing posts with label K6. Show all posts
Showing posts with label K6. Show all posts

Friday, 16 October 2026

Performance Testing with K6 and Playwright

 

Performance Testing with K6 and Playwright

A step-by-step guide from setup to execution—combining functional browser journeys with realistic performance testing.

K6PlaywrightBrowser performanceCI/CD ready
In brief: Playwright validates that a user journey works. k6 measures how that journey behaves under load. This guide shows how to use both in one JavaScript-oriented workflow.

Why Combine K6 with Playwright?

Modern web applications must be fast and functionally correct under real-world traffic. Playwright is excellent for end-to-end verification, while k6 is purpose-built for load and performance testing. K6’s experimental browser module provides a Playwright-inspired API for browser-level performance checks.

Playwrightk6
Primary purposeFunctional and E2E testingLoad and performance testing
Execution modelReal browser, single/few sessionsThousands of VUs at protocol level, or smaller browser-level loads
ScriptingJavaScript/TypeScript, Python, Java, .NETJavaScript (Goja runtime)
MetricsPass/fail assertionsLatency, throughput, error rate, thresholds
Browser APINativek6/experimental/browser, Playwright-inspired

Pairing the tools lets teams reuse selectors and user flows, validate critical journeys such as login or checkout, capture browser metrics alongside load metrics, and run tests from CI/CD.

Prerequisites and Setup

You’ll need Node.js v16 or later with npm, a code editor, optional Git, basic JavaScript and selector knowledge, and administrator or sudo access for k6 and browser dependencies.

Initialize a Node.js project

mkdir k6-playwright-perf-test
cd k6-playwright-perf-test
npm init -y

Install Playwright

npm install -D @playwright/test
npx playwright install --with-deps

Keep a Playwright functional suite in the repository so the journey can be verified before it is performance-tested.

Install k6

# macOS
brew install k6

# Windows
choco install k6

# Debian / Ubuntu
sudo apt-get update
sudo apt-get install k6

# Docker alternative
docker pull grafana/k6

k6 version

Enable the browser module

Modern k6 releases bundle the experimental browser module. A recent release such as k6 v0.47.0 or later is recommended. Older releases may require an xk6 custom build:

go install go.k6.io/xk6/cmd/xk6@latest
xk6 build --with github.com/grafana/xk6-browser@latest

Recommended project structure

k6-playwright-perf-test/
├── playwright-tests/
│   └── login.spec.js
├── k6-tests/
│   └── login.perf.js
├── package.json
└── playwright.config.js

Writing the Playwright Functional Test

First validate the login flow functionally, then translate the same selectors and steps into the k6 script.

// playwright-tests/login.spec.js
const { test, expect } = require('@playwright/test');

test('user can log in', async ({ page }) => {
  await page.goto('https://test.k6.io/my_messages.php');
  await page.fill('#login', 'admin');
  await page.fill('#password', '123');
  await page.click('input[type="submit"]');
  await expect(page.locator('h3')).toContainText('Welcome, admin!');
});
npx playwright test playwright-tests/login.spec.js

Writing the K6 Performance Test

import { browser } from 'k6/experimental/browser';
import { check } from 'k6';

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      vus: 5,
      iterations: 10,
      options: { browser: { type: 'chromium' } },
    },
  },
  thresholds: {
    checks: ['rate==1.0'],
    browser_http_req_duration: ['p(95)<2000'],
  },
};

export default async function () {
  const page = browser.newPage();
  try {
    await page.goto('https://test.k6.io/my_messages.php');
    await page.locator('#login').type('admin');
    await page.locator('#password').type('123');
    await Promise.all([
      page.waitForNavigation(),
      page.locator('input[type="submit"]').click(),
    ]);
    const heading = page.locator('h3');
    check(heading, {
      'welcome message is visible': (h) =>
        h.textContent().includes('Welcome, admin!'),
    });
  } finally {
    page.close();
  }
}

Key concepts: browser.newPage() creates a browser page, page.locator() mirrors Playwright’s selector model, scenarios define the load shape, thresholds define objective pass/fail criteria, and check() records assertions without acting like a test-runner exception.

Configuring Test Options

export const options = {
  scenarios: {
    ui: {
      executor: 'ramping-vus',
      startVUs: 1,
      stages: [
        { duration: '30s', target: 5 },
        { duration: '1m', target: 5 },
        { duration: '30s', target: 0 },
      ],
      options: { browser: { type: 'chromium' } },
    },
  },
  thresholds: {
    browser_http_req_duration: ['p(90)<1500', 'p(95)<2500'],
    checks: ['rate>0.95'],
  },
};
  • Executors: Choose shared-iterations, per-vu-iterations, constant-vus, ramping-vus, constant-arrival-rate, or ramping-arrival-rate according to the desired concurrency or arrival rate.
  • Stages: Ramp VUs gradually. Browser VUs are resource-intensive; keep counts modest, typically under 10–20 concurrent browser VUs per machine.
  • Thresholds: Tie automated pass/fail behavior to measurable SLAs.

Executing the Performance Test

k6 run k6-tests/login.perf.js

# Docker
docker run --rm -i -v $(pwd):/scripts grafana/k6 \
  run /scripts/k6-tests/login.perf.js

# JSON output
k6 run --out json=results.json k6-tests/login.perf.js

# InfluxDB output
k6 run --out influxdb=http://localhost:8086/k6 k6-tests/login.perf.js

Analyzing and Interpreting Results

checks.........................: 100.00% ✓ 10  ✗ 0
browser_http_req_duration......: avg=812ms p(95)=1.3s
browser_web_vital_lcp...........: avg=1.1s
iteration_duration..............: avg=2.3s
vus..............................: 5
  • checks: The pass rate of all check() assertions.
  • browser_http_req_duration: Time taken by browser-initiated HTTP requests.
  • browser_web_vital_*: Browser-level LCP, FCP, and CLS metrics when supported.
  • iteration_duration: Total duration of a complete script iteration.
  • Threshold results: k6 returns a non-zero exit status when a threshold fails, making it suitable for CI/CD gates.

Best Practices and Common Pitfalls

  • Keep browser-level VUs low. Use protocol-level k6 tests for high concurrency and browser tests for representative journeys.
  • Separate concerns. Use Playwright for strict functional assertions and cross-browser coverage; use k6 for performance-focused checks.
  • Use realistic think-time. Prefer wait-for-selector patterns or carefully chosen pauses to mimic user pacing.
  • Set meaningful thresholds tied to actual service-level objectives.
  • Run in CI/CD and use k6’s exit code as a quality gate.
  • Isolate test data with unique users or records per VU and iteration.
  • Monitor the test runner. CPU and memory limits can bottleneck the generator before the target system.

Conclusion

Combining k6 with a Playwright-style browser API provides a consistent way to validate functional correctness and real-world performance. Verify the flow with Playwright, translate it to a k6 browser script, define a realistic scenario and thresholds, execute it, and use the resulting metrics to catch regressions early.

Frequently Asked Questions

Is k6’s browser module the same as Playwright?

No. It is a Playwright-inspired API designed for performance testing in k6. Playwright remains the dedicated functional and E2E tool.

Can I run thousands of virtual users with the browser module?

Browser-level VUs are resource-heavy. Use protocol-level k6 tests for high-concurrency load and browser-level tests for smaller, representative populations.

Do I need Playwright installed to use k6’s browser module?

No. k6 runs its browser module independently. Playwright is complementary and useful for validating flows and reusing selectors.

Can these tests run in a CI/CD pipeline?

Yes. Both tools are CLI-driven and return process exit codes suitable for GitHub Actions, GitLab CI, Jenkins, and similar systems.

Version note: The browser module is experimental and its API may change. Verify code samples and configuration against the current official k6 and Playwright documentation before publishing or adopting them in production.

Translate







Tweet