You are reading the fourth post in the performance testing series. In case you missed the previous post, here they are:
k6 is one of the first performance testing programs to enable both protocol and browser testing. You may now execute your web application's performance test in the web browser, just like any other user. It is a game-changer in the load testing arena.
This functionality allows for simulating real user scenarios more accurately, capturing performance metrics, and analysing web application behaviour comprehensively.
Using this feature, you can receive browser-specific metrics like total page load time. It makes sure that all elements are interactive, checks for loading spinners that take a long time to disappear, and monitors how the front end responds to thousands of simultaneous protocol-level requests.
ℹTake notice! This functionality is currently in the experimental stage, and k6 says it is still working to make the module stable. To use this feature, make sure you are using the latest k6 version and have installed a Chromium-based browser. Knowing this, let's move on to exploration.
Before anything else, create a new js file and import the browser module. In the options topic, the executor and browser type are mandatory, and the browser type must be set to 'chromium'. You can select a variety of executors; for the first example, I used 'shared-iterations'.
const page = browser.newPage();
If you want to change the size of the browser window, you can add the following piece of code.
page.setViewportSize({
width: 1425,
height: 1818
});
After getting the page, you can interact with it. In this example, I visit a url, take a screenshot of the page, and close the page.
export default async function(){
const page = browser.newPage();
page.setViewportSize({
width: 1425,
height: 1818
});
await page.goto('https://hotel.testplanisphere.dev/en-US/login.html');
page.screenshot({path: 'screenshot.png'});
page.close();
}
Let's run the script with this command: k6 run script.js You didn’t see the browser, did you? k6 has default arguments when launching the browser and the headless default value is true. If you want to change this you can use this command:
K6_BROWSER_HEADLESS=false k6 run script.js
It’s time to improve the script to interact with elements on the page such as click, select, and type. The browser module supports CSS and XPath selectors.
page.locator("input[name='email']").type('clark@example.com');
page.locator("#password").type('pasword');
page.locator('#login-button').click();
Pass the selector of the element you want to find on the page to page.locator(). page.locator() will create and return a locator object, which you can later use to interact with the element. Example of the above, it finds the email element and writes ‘clark@example.com’, finds the password element and writes ‘password’ and clicks the login button. You can also write these scripts using the syntax below. The functionality is the same, but I find the markup simpler and more reusable.
const emailTextBox = page.locator("input[name='email']");
emailTextBox.type("clark@example.com");
const passwordTextBox = page.locator("#password");
passwordTextBox.type("pasword");
const loginButton = page.locator('#login-button');
loginButton.click();
Let’s complete the script by adding some validations. k6 provides an assertion structure with 'check' that is similar to other framework assertions. Unlike others, failed checks do not cause the test to abort or end with a failed status. k6 keeps track of the number of failed checks as the test runs.
check(page, {
'login is successful':
page.locator("#logout-form").isVisible() === true,
'page title is correct':
page.title().includes('MyPage')
})
After running the script, you can see the results of the logout element being visible on the page and the assertions about the page title among its outputs.
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: script.js
output: -
scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
* ui: 1 iterations shared among 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)
✓ login is successful
✓ page title is correct
If the checker gets an error, you can see it in the terminal as follows.
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: scripts/browserTest.js
output: -
scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
* ui: 1 iterations shared among 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)
✓ login is successful
✗ page title is correct
↳ 0% -- ✓ 0 / ✗ 1
You can run both browser-level and protocol-level tests in a single script by using the same steps. In this way, you can track the condition of your APIs under load and learn about how the front end performs.
import { browser } from 'k6/experimental/browser';
import { check } from 'k6';
import http from 'k6/http';
export const options = {
scenarios: {
browser: {
executor: 'constant-vus',
exec: 'browserTest',
vus: 3,
duration: '10s',
options: {
browser: {
type: 'chromium',
},
},
},
api: {
executor: 'constant-vus',
exec: 'api',
vus: 20,
duration: '1m',
},
},
};
export async function browserTest() {
const page = browser.newPage();
try {
await page.goto('https://test.k6.io/browser.php');
page.locator('#checkbox1').check();
check(page, {
'checkbox is checked':
page.locator('#checkbox-info-display').textContent() === 'Thanks for checking the box',
});
} finally {
page.close();
}
}
export function api() {
const res = http.get('https://test.k6.io/news.php');
check(res, {
'status is 200': (r) => r.status === 200,
});
}
In the previous blog post, I examined the metrics in detail. Now, I will explain the metrics specific to the browser.
browser_data_received.......: 712 kB 281 kB/s
browser_data_sent...........: 4.4 kB 1.7 kB/s
browser_http_req_duration...: avg=46ms min=4.01ms med=26.11ms max=138.57ms p(90)=129.53ms p(95)=134.05ms
browser_http_req_failed.....: 9.09% ✓ 1 ✗ 10
browser_web_vital_cls.......: avg=0.042967 min=0.042967 med=0.042967 max=0.042967 p(90)=0.042967 p(95)=0.042967
browser_web_vital_fcp.......: avg=188.75ms min=168.8ms med=188.75ms max=208.7ms p(90)=204.71ms p(95)=206.7ms
browser_web_vital_fid.......: avg=1.7ms min=1.7ms med=1.7ms max=1.7ms p(90)=1.7ms p(95)=1.7ms
browser_web_vital_lcp.......: avg=188.75ms min=168.8ms med=188.75ms max=208.7ms p(90)=204.71ms p(95)=206.7ms
browser_web_vital_ttfb......: avg=55.1ms min=26.29ms med=55.1ms max=83.9ms p(90)=78.14ms p(95)=81.02ms
checks......................: 100.00% ✓ 2 ✗ 0
data_received...............: 0 B 0 B/s
data_sent...................: 0 B 0 B/s
iteration_duration..........: avg=1.53s min=1.53s med=1.53s max=1.53s p(90)=1.53s p(95)=1.53s
iterations..................: 1 0.394187/s
vus.........................: 1 min=1 max=1
vus_max.....................: 1 min=1 max=1
- browser_data_received: The amount of data received by the browser.
- browser_data_sent: The amount of data sent by the browser.
- browser_http_req_duration: Average duration of HTTP requests
- browser_http_req_failed: The percentage of failed HTTP requests.
- browser_web_vital_cls: Cumulative Layout Shift, a measure of visual stability.
- browser_web_vital_fcp: First Contentful Paint, the time it takes for the first piece of content to be rendered.
- browser_web_vital_fid: First Input Delay, the time it takes for the page to respond to the first user interaction.
- browser_web_vital_lcp: Largest Contentful Paint, the time it takes for the largest content element to be rendered.
- browser_web_vital_ttfb: Time to First Byte, the time it takes for the browser to receive the first byte of the response from the server.
k6 represents a new stage in the field of load testing with the browser module. By bridging the gap between protocol-level and browser-level testing, k6 enables organisations to gain insight into the end-user experience. You can collect browser-specific metrics and ensure that everything is performed properly on the frontend, even under load.
While the k6 browser module is still in its early stages, it is possible to perform comprehensive load testing while also combining protocol-level and browser-level tests into a single script.
As the demand for high-performance, user-friendly web applications continues to grow, the k6 Browser module positions itself as a toolkit that equips developers, performance engineers and QA professionals with the tools necessary to deliver digital ecosystems.