<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marie Cruz</title>
    <description>The latest articles on DEV Community by Marie Cruz (@mdcruz).</description>
    <link>https://dev.to/mdcruz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1006047%2F0e938037-7559-4537-995c-a857d676cbad.png</url>
      <title>DEV Community: Marie Cruz</title>
      <link>https://dev.to/mdcruz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mdcruz"/>
    <language>en</language>
    <item>
      <title>Get Started with k6 browser</title>
      <dc:creator>Marie Cruz</dc:creator>
      <pubDate>Wed, 11 Jan 2023 11:28:57 +0000</pubDate>
      <link>https://dev.to/k6/get-started-with-xk6-browser-2bpe</link>
      <guid>https://dev.to/k6/get-started-with-xk6-browser-2bpe</guid>
      <description>&lt;p&gt;This post explains how to get started with &lt;a href="https://github.com/grafana/xk6-browser" rel="noopener noreferrer"&gt;k6 browser&lt;/a&gt;, a &lt;a href="https://k6.io/docs/javascript-api/#k6-experimental" rel="noopener noreferrer"&gt;k6 experimental module&lt;/a&gt; that adds browser-level APIs to interact with browsers and collect web performance metrics as part of your k6 tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Beyond Protocol Level
&lt;/h2&gt;

&lt;p&gt;Over the years, k6 has become known as a performance testing tool that provides the best developer experience. Most of our efforts have focused on providing a tool that helps test your servers or backend systems. Our &lt;a href="https://k6.io/docs/testing-guides/load-testing-websites/" rel="noopener noreferrer"&gt;comprehensive load testing guide&lt;/a&gt; recommends keeping users in mind, and backend performance testing only addresses half of your performance testing efforts. &lt;/p&gt;

&lt;p&gt;Suppose you test the user experience of your website and verify that there are no performance issues on a specific user journey. In that case, &lt;strong&gt;you need to drive some of your performance testing efforts from a browser perspective and consider a more realistic end-to-end test of the user flow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most load-testing tools focus on testing API endpoints, but it's different from what your users normally interact with. Your users interact with the browser, so it's also vital to test the browser's performance to get an end-to-end perspective of what's happening when interacting with your web applications.&lt;/p&gt;

&lt;p&gt;Both frontend and backend performance testing has its pros and cons when done in isolation, which we discussed in more detail as part of the video below. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/xVACRP5qIJI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here at k6, we want to start expanding our performance testing use case and also test beyond the protocol level.&lt;/p&gt;

&lt;p&gt;This is where the k6 browser module comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is k6 browser module?
&lt;/h2&gt;

&lt;p&gt;The browser module brings browser automation and end-to-end web testing to k6 while supporting core k6 features. It enables you to get insights from your front-end application during performance testing.&lt;/p&gt;

&lt;p&gt;You can now mix browser-level and protocol-level tests in a single and unified script using k6. This can simulate the bulk of your traffic from your protocol-level tests and run one or two virtual users on a browser level to mimic how a user interacts with your website, thus leveraging a hybrid approach to performance testing.&lt;/p&gt;

&lt;p&gt;The browser module offers a unique solution as you don’t have to use separate tools to test your frontend and backend systems. It also offers a simplified experience and aggregated view of performance metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the test
&lt;/h3&gt;

&lt;p&gt;You can copy one of our example scripts to get started as part of our &lt;a href="https://k6.io/docs/javascript-api/k6-browser/get-started/running-browser-tests/" rel="noopener noreferrer"&gt;k6 browser module documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/experimental/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what’s happening with the preceding code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We are importing &lt;code&gt;chromium&lt;/code&gt; from the &lt;code&gt;k6/experimental/browser&lt;/code&gt; module. &lt;code&gt;chromium&lt;/code&gt; is of type &lt;a href="https://k6.io/docs/javascript-api/xk6-browser/api/browsertype/" rel="noopener noreferrer"&gt;&lt;code&gt;BrowserType&lt;/code&gt;&lt;/a&gt;, which is k6 browser’s entry point into launching a browser process.&lt;/li&gt;
&lt;li&gt;Next, we use the &lt;a href="https://k6.io/docs/using-k6/test-lifecycle/#the-vu-stage" rel="noopener noreferrer"&gt;scenario function&lt;/a&gt;, an existing k6 functionality, to define our VU (virtual user) code.&lt;/li&gt;
&lt;li&gt;To create a new browser instance, we use the &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/browsertype/launch/" rel="noopener noreferrer"&gt;&lt;code&gt;launch&lt;/code&gt;&lt;/a&gt; method of &lt;code&gt;chromium&lt;/code&gt;, which returns a &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/browser/" rel="noopener noreferrer"&gt;&lt;code&gt;Browser&lt;/code&gt;&lt;/a&gt; object. You can pass different parameters within &lt;code&gt;launch&lt;/code&gt; and one of the parameters you can pass is &lt;code&gt;headless&lt;/code&gt;, which you can use to show the browser or not.&lt;/li&gt;
&lt;li&gt;To create a new page in your browser instance, we use the &lt;code&gt;browser.newPage()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, on to the fun part! Let’s simulate a user visiting a &lt;a href="https://test.k6.io/" rel="noopener noreferrer"&gt;test application&lt;/a&gt; and logging in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/experimental/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://test.k6.io/my_messages.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="login"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="password"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForNavigation&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[type="submit"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome, admin!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a lot of things happening in the preceding code especially the introduction of &lt;a href="https://k6.io/docs/javascript-api/k6-browser/get-started/running-browser-tests/#asynchronous-operations" rel="noopener noreferrer"&gt;asynchronous operations&lt;/a&gt; so let’s break it down again.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We visit the page by using &lt;code&gt;page.goto&lt;/code&gt; and pass the test application URL. We are also waiting for the network to be idle, which will succeed if there are no network connections for at least 500 ms. &lt;code&gt;page.goto&lt;/code&gt; is also an asynchronous operation, so we need to wait for this to finish and use the &lt;code&gt;await&lt;/code&gt; keyword.&lt;/li&gt;
&lt;li&gt;Once the operation completes, we use &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/locator/" rel="noopener noreferrer"&gt;&lt;code&gt;page.locator&lt;/code&gt;&lt;/a&gt; to interact with the elements we want. In the example, we are creating two locators. One for the login name and another one for the login password. We use the &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/locator/type/" rel="noopener noreferrer"&gt;&lt;code&gt;type&lt;/code&gt;&lt;/a&gt; method to type the name and password into the fields.&lt;/li&gt;
&lt;li&gt;To click the login button, we use the &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/locator/click/" rel="noopener noreferrer"&gt;&lt;code&gt;click&lt;/code&gt;&lt;/a&gt; method, which is an asynchronous operation. Clicking the submit button also causes page navigation which we need to wait to load, so &lt;code&gt;page.waitForNavigation()&lt;/code&gt;, another asynchronous operation, is needed because the page won't be ready until the navigation completes.&lt;/li&gt;
&lt;li&gt;Since there are two asynchronous operations, we need to use &lt;code&gt;Promise.all([])&lt;/code&gt; to wait for the two promises to be resolved before continuing to avoid any race conditions.&lt;/li&gt;
&lt;li&gt;Next, we use the &lt;a href="https://k6.io/docs/javascript-api/k6/check/" rel="noopener noreferrer"&gt;check&lt;/a&gt; feature from k6 to assert the text content of a specific element.&lt;/li&gt;
&lt;li&gt;Finally, we close the page and the browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Running the Test
&lt;/h3&gt;

&lt;p&gt;To run the test, simply use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;K6_BROWSER_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;k6 run script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you face any issues running the command, please check out our &lt;a href="https://k6.io/docs/javascript-api/k6-browser/get-started/running-browser-tests/#run-a-test" rel="noopener noreferrer"&gt;documentation for running the test&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should see a similar test run as this &lt;a href="https://user-images.githubusercontent.com/10811379/221546009-1292553c-1eec-4982-a97f-6e9d26531484.mp4" rel="noopener noreferrer"&gt;video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the browser launching, this provides a more visual experience as to what your users actually see so you can also find blind spots and catch issues related to browsers that won't be detected on a protocol-level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Metrics
&lt;/h3&gt;

&lt;p&gt;When it's finished running, apart from the metrics that k6 already tracks, additional &lt;a href="https://k6.io/docs/javascript-api/k6-browser/get-started/browser-metrics/" rel="noopener noreferrer"&gt;browser metrics&lt;/a&gt; are tracked as part of the k6 summary output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  browser_dom_content_loaded.......: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;36.72ms &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;544µs   &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22.61ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;87.02ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;74.14ms  p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;80.58ms
  browser_first_contentful_paint...: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;47.52ms &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22.01ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;47.52ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;73.02ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;67.92ms  p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;70.47ms
  browser_first_meaningful_paint...: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75.22ms &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75.22ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75.22ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75.22ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;75.22ms  p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;75.22ms
  browser_first_paint..............: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;45.72ms &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21.96ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;45.72ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;69.49ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;64.73ms  p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;67.11ms
  browser_loaded...................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;38.14ms &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5.28ms  &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22.45ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;86.68ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;73.83ms  p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;80.26ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The metrics above are still subject to change. Our goal is to be able to report the &lt;a href="https://web.dev/vitals/#core-web-vitals" rel="noopener noreferrer"&gt;Core Web Vitals&lt;/a&gt; as well as &lt;a href="https://web.dev/vitals/#other-web-vitals" rel="noopener noreferrer"&gt;Other Web Vitals&lt;/a&gt; for a better understanding of user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  k6 Browser API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://k6.io/docs/javascript-api/k6-browser/api/" rel="noopener noreferrer"&gt;k6 Browser API&lt;/a&gt; aims to provide a rough compatibility with the &lt;a href="https://playwright.dev/docs/api/class-playwright" rel="noopener noreferrer"&gt;Playwright API&lt;/a&gt; for NodeJS, meaning k6 users don't have to learn an entirely new API.&lt;/p&gt;

&lt;p&gt;At the moment, the k6 API is synchronous. However, since many browser operations happen asynchronously, and in order to follow the Playwright API more closely, we are working on migrating most of the browser methods to be asynchronous. This means that while k6 browser is ready to be used, be warned that our API is still undergoing a few changes.&lt;/p&gt;

&lt;p&gt;At the moment, few methods such as &lt;code&gt;page.goto()&lt;/code&gt;, &lt;code&gt;page.waitForNavigation()&lt;/code&gt; and &lt;code&gt;Locator.click()&lt;/code&gt; return &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises" rel="noopener noreferrer"&gt;JavaScript promises&lt;/a&gt;. Our goal is to support async and await syntax for all asynchronous operations, for simplicity.&lt;/p&gt;

&lt;p&gt;For more examples on how to use the k6 browser API, please check out &lt;a href="https://github.com/grafana/xk6-browser/tree/main/examples" rel="noopener noreferrer"&gt;k6 browser examples&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Hybrid Approach to Performance Testing
&lt;/h2&gt;

&lt;p&gt;If you only consider web performance, this can lead to false confidence in overall application performance when the amount of traffic against an application increases.&lt;/p&gt;

&lt;p&gt;It's still highly recommended to also test your backend systems to have a complete picture of your application’s performance, via the protocol-level.&lt;/p&gt;

&lt;p&gt;However, there are problems associated with testing via the protocol level, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not being closer to the user experience since it’s skipping the browser,&lt;/li&gt;
&lt;li&gt;scripts can be lengthy to create and difficult to maintain as the application grows,&lt;/li&gt;
&lt;li&gt;browser performance metrics are ignored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, if you perform load testing by spinning up a lot of browsers, this requires significantly more load-generation resources, which can end up quite costly.&lt;/p&gt;

&lt;p&gt;To address the shortcomings of each approach, a recommended practice is to adopt a &lt;a href="https://k6.io/docs/testing-guides/load-testing-websites/#hybrid-load-testing" rel="noopener noreferrer"&gt;hybrid approach to performance testing&lt;/a&gt;, which is a combination of testing both the backend and frontend systems via protocol and browser level. With a hybrid approach, you spin up the majority of your load via the protocol level, then simultaneously have one or two browser-level virtual users, so you can also have a view of what’s happening on the front end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5ougnent4lykfbgsp9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5ougnent4lykfbgsp9r.png" alt="An illustration of k6 testing frontend system as well as backend systems for a hybrid approach to performance testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The great thing with k6 browser module is that it can offer you this hybrid approach to performance testing. While you can do this with multiple tools, the beauty of using this module is that it's built on top of k6, which means that you can have a protocol-level and a browser-level test in the same script.&lt;/p&gt;

&lt;p&gt;Let's see how that translates to code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the test
&lt;/h3&gt;

&lt;p&gt;A common scenario that we recommend is to mix a smaller subset of browser-level tests with a larger protocol-level test. To run a browser-level and protocol-level test concurrently, you can use &lt;a href="https://k6.io/docs/using-k6/scenarios/" rel="noopener noreferrer"&gt;scenarios&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/experimental/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constant-vus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;news&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constant-vus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;news&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://test.k6.io/browser.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#checkbox1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkbox is checked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#checkbox-info-display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thanks for checking the box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;news&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://test.k6.io/news.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status is 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the preceeding code again.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We are using &lt;a href="https://k6.io/docs/using-k6/k6-options/" rel="noopener noreferrer"&gt;options&lt;/a&gt; to configure our test-run behaviour. In this particular script, we are declaring two scenarios to configure specific workload, one for the browser-level test called &lt;code&gt;browser&lt;/code&gt; and one for the protocol-level test called &lt;code&gt;news&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Both the &lt;code&gt;browser&lt;/code&gt; and &lt;code&gt;news&lt;/code&gt; scenario are using the &lt;a href="https://k6.io/docs/using-k6/scenarios/executors/constant-vus/" rel="noopener noreferrer"&gt;constant-vu executor&lt;/a&gt; which introduces a constant number of virtual users to execute as many iterations as possible for a specified amount of time.&lt;/li&gt;
&lt;li&gt;Next, there are two JavaScript functions declared, &lt;code&gt;browser()&lt;/code&gt; and &lt;code&gt;news()&lt;/code&gt;. These functions contain the code that will be executed by a virtual user. The &lt;code&gt;browser()&lt;/code&gt; function, represents our browser-level test and simply visits a test URL, clicks a checkbox and verifies if the checkbox has been ticked successfully while the &lt;code&gt;news()&lt;/code&gt; function, which represents our protocol-level test, sends a GET request to a different URL and checks if the status code is returning 200.&lt;/li&gt;
&lt;li&gt;Since we are using scenarios, the two functions are independent from each other and therefore, runs in parallel.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Running the test
&lt;/h3&gt;

&lt;p&gt;Using the same k6 run command as above, you should see a similar test output as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;running &lt;span class="o"&gt;(&lt;/span&gt;1m00.1s&lt;span class="o"&gt;)&lt;/span&gt;, 00/21 VUs, 12953 &lt;span class="nb"&gt;complete &lt;/span&gt;and 0 interrupted iterations
browser ✓ &lt;span class="o"&gt;[======================================]&lt;/span&gt; 1 VUs   10s
news    ✓ &lt;span class="o"&gt;[======================================]&lt;/span&gt; 20 VUs  1m0s

     ✓ status is 200
     ✓ checkbox is checked

     browser_dom_content_loaded.......: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12.27ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;68µs    &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;11.76ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;26.77ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;25.56ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;26.36ms
     browser_first_contentful_paint...: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21.93ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12.5ms  &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25.32ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;26.19ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;25.83ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;26.01ms
     browser_first_paint..............: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21.88ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12.45ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25.27ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;26.14ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;25.78ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;25.96ms
     browser_loaded...................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12.18ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;984µs   &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;11.74ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25.65ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;24.37ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;25.19ms
     checks...........................: 100.00% ✓ 12953      ✗ 0
     data_received....................: 21 MB   341 kB/s
     data_sent........................: 1.4 MB  24 kB/s
     http_req_blocked.................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2.14ms   &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1µs     &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2µs     &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;290.37ms p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;4µs     p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;4µs
     http_req_connecting..............: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.09ms   &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s      &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s      &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;195ms    p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;0s      p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;0s
     http_req_duration................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90.59ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80.93ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;92.8ms  &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;542.92ms p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;96.89ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;102.83ms
       &lt;span class="o"&gt;{&lt;/span&gt; expected_response:true &lt;span class="o"&gt;}&lt;/span&gt;.....: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90.49ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80.93ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;92.8ms  &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;542.92ms p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;96.86ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;102.81ms
     http_req_failed..................: 0.00%   ✓ 0          ✗ 12946
     http_req_receiving...............: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;154.41µs &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;17µs    &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;47µs    &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;97ms     p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;67µs    p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;76µs
     http_req_sending.................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20.36µs  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s      &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14µs    &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32.36ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;20µs    p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;21µs
     http_req_tls_handshaking.........: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.16ms   &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s      &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s      &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;183.1ms  p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;0s      p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;0s
     http_req_waiting.................: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90.41ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80.85ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;92.72ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;542.86ms p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;96.77ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;102.74ms
     http_reqs........................: 12960   215.658713/s
     iteration_duration...............: &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;93.58ms  &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;81.05ms &lt;span class="nv"&gt;med&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;92.95ms &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.83s    p&lt;span class="o"&gt;(&lt;/span&gt;90&lt;span class="o"&gt;)=&lt;/span&gt;97.54ms p&lt;span class="o"&gt;(&lt;/span&gt;95&lt;span class="o"&gt;)=&lt;/span&gt;103.37ms
     iterations.......................: 12953   215.542231/s
     vus..............................: 20      &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20       &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21
     vus_max..........................: 21      &lt;span class="nv"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21       &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since it's all in one script, this allows for greater collaboration amongst teams and a unified view of the performance metrics from a browser-level and protocol-level perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;While k6 browser started off as an &lt;a href="https://k6.io/docs/extensions/" rel="noopener noreferrer"&gt;extension&lt;/a&gt;, as of &lt;a href="https://github.com/grafana/k6/releases/tag/v0.43.0" rel="noopener noreferrer"&gt;k6 version 0.43.0&lt;/a&gt;, it is now bundled in k6 as an experimental module, and usable without a separate binary or compilation step! We also have further plans to integrate k6 browser in k6 cloud as part of a private beta. We consider browser automation an important part of web application testing, and we have big goals for k6 browser. Our &lt;a href="https://github.com/grafana/xk6-browser/blob/main/ROADMAP.md" rel="noopener noreferrer"&gt;roadmap&lt;/a&gt; details essential status updates and our short, mid, and long-term goals.&lt;/p&gt;

&lt;p&gt;With that said, we need your help! Since k6 browser is still relatively new, we need help from the community to try out the tool and give us feedback.&lt;/p&gt;

&lt;p&gt;Check our &lt;a href="https://github.com/grafana/xk6-browser" rel="noopener noreferrer"&gt;GitHub project&lt;/a&gt;, read our &lt;a href="https://k6.io/docs/javascript-api/k6-browser/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, and play with the tool. If you find any issues, please raise them on our GitHub project or check out our &lt;a href="https://community.k6.io/c/xk6-browser/14" rel="noopener noreferrer"&gt;community forum&lt;/a&gt; for additional support.&lt;/p&gt;

&lt;h2&gt;
  
  
  See Also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://k6.io/docs/testing-guides/load-testing-websites" rel="noopener noreferrer"&gt;A k6 guide for load testing websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=xVACRP5qIJI" rel="noopener noreferrer"&gt;Video: Frontend vs. backend performance testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k6.io/docs/javascript-api/k6-browser/" rel="noopener noreferrer"&gt;k6 browser documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>k6</category>
    </item>
  </channel>
</rss>
