TESTEVERYTHING

Showing posts with label Performance. Show all posts
Showing posts with label Performance. 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.

Tuesday, 22 December 2015

Common Issues While Creating Performance Scripts

Common Issues While Creating Performance Scripts

When you start load testing a web site (especially if this is the first load test in your life) you may see a lot of errors in the report. Possibly you will even have to stop the test before it completes, because at some point in time it becomes clear that something goes wrong. If the problem is not so obvious, it is still recommended to check the report for the errors related to each virtual user profile (i.e. to each different type of virtual users) before looking at any performance data.
Issue 1:  
Most probably the errors appear because you have not parameterized the HTTP requests in your profile. Note that initially all requests are recorded as is. If user use a recorded profile without any modifications. Performance tool will simply repeat all the same requests with the same values of all parameters. However some values should be unique for each session.
You can check the “Logs” folder for errors and find the first request that produced an error. The goal is to find dynamic session-specific values inside that request. You can do this by comparing different sessions. Usually such session-specific values are contained inside the request parameters, but they can also be passed inside the main part of the URL. When you find such value you also need to find a place (the body of one of the previous responses) from which you can extract it. You can use the “Edit | Find and Replace…” feature for that.
Issue 2:  
Let’s imagine that you have done good work on the parameterization and now when you verify your profile, it does not produce any errors, and each new session completes as expected. Now you try to execute a small test with several concurrent users, and you see that it fails for all users except the first one. The problem may be that your web site does not allow several concurrent sessions with the same user credentials. This means that you need to use a different user name and password for each virtual user.
After you make all necessary modifications to the profile you can enable the full logging to check that all user names and passwords are passed correctly. By default logging is enabled only for verification, but if you run test with just few users, you can switch to full logging without any significant performance overhead.
Issue 3:  
Even in case the user credentials are passed correctly, you may still see that almost all sessions complete with errors. This may be because some session-specific values still require parameterization, because they depend on the user account. Basically this is the same problem as the first one. I only wanted to note that you may face it again when you switch from one user to many users test.
For example, it may be that each user of your system has a special ID which is passed in some parameter of each request. If you use a different user name, you also need to change that ID. As usual the right value can be extracted from some response and assigned to a variable.
Issue 4:  
Your web application may require authentication. This is not exactly the same as entering the user name and password in some form on a web page and clicking the “Sign In” button. When you use a browser to access a site requiring authentication, it displays a separate dialog asking for credentials. Since this is done in a secure manner, it is not possible to record the credentials automatically
Issue 5:  
There is one very important rule that you should apply when creating a new virtual user profile: always start recording by entering the landing page URL into the address bar and always delete cookies and browser cache before recording. This is not just a good advice; this is the only correct practice.
If you start recording from the middle of a user session, your load testing tool will not be able to reproduce that session correctly. Even if you click a link on a page already open in your browser instead of manually entering the address, this can produce incorrect recording.
The same will happen if you do not delete cookies. Your web site may behave differently for new users and for returning ones. It may use cookies to restore the previous session. If you record such restored session, you will not be able to emulate a new one.
Finally, if you do not delete browser cache, all requests to cached elements will be skipped on recording. This is the least important problem, because any session-specific requests will be sent in any case, but to create a realistic load you will probably want to load page elements as well.
Important Note:
If you see errors in the report, you should definitely research the problem as described above. However even if you do not see any red lines in the log, this does not mean that everything works correctly. Your web site can produce errors in a “user friendly” manner or just redirect to a different page if it cannot provide valid content for a request containing incorrect parameters. This means that you need to read the actual response to see if it is correct. Again, you can use the “Compare with recorded” function and check all responses step by step.

Wednesday, 11 March 2015

What qualities should have in your automation framework


The following are some qualities should have in your automation framework.

  • Maintainability 
  • Portability 
  • Flexibility 
  • Robustness 
  • Scalability 
  • Reliability 
  • Usability 
  • Performance

Translate







Tweet