Selenium Automation | Do Anything You Want!

Introduction to Chrome DevTools

Chrome DevTools is a set of tools built directly into Chromium-based browsers such as Chrome, Opera, and Microsoft Edge to help developers debug and research websites.

With Chrome DevTools, developers can gain deeper access to websites and be able to:

  • Inspect elements in the DOM
  • Edit elements and CSS on the fly
  • Check and monitor website performance
  • Simulate the user's geographic location
  • Simulate faster/slower network speeds
  • Execute and debug JavaScript
  • View console logs
  • etc.

Selenium 4 Chrome DevTools API

picture

Selenium is a comprehensive project of tools and libraries that support web browser automation. Selenium 4 adds native support for the Chrome DevTools API. With these new APIs, our tests can now:

  • Capture and monitor network traffic and performance
  • Simulate geographic locations for location-aware testing, localization and internationalization testing
  • Change device modes and test app responsiveness

And that's just the tip of the iceberg!

Selenium 4 introduces the new ChromiumDriver class, which includes two methods for accessing Chrome DevTools: getDevTools() and executeCdpCommand().

The getDevTools() method returns a new DevTools object that allows you to send built-in Selenium commands for CDP using the send() method. These commands are wrappers that make calling CDP functions clearer and easier.

The executeCdpCommand() method also allows you to execute CDP methods, but is more primitive. Instead of using a wrapped API, it allows you to pass in a Chrome DevTools command and the parameters for that command directly. If there is no Selenium wrapper API for a CDP command, or if you want to invoke it differently than the Selenium API, you can use executeCdpCommand().

Chromium-based drivers like ChromeDriver and EdgeDriver now inherit from ChromiumDriver, so you can access the Selenium CDP API from those drivers as well.

Let's explore how these new Selenium 4 APIs can be leveraged to address various use cases.

Simulate device mode

Most of the apps we build today are responsive to meet the needs of end users from various platforms, devices (e.g. mobile, tablet, wearable, desktop) and screen orientations.

As testers, we may wish to place our application in different sizes to trigger the responsiveness of the application.

How can we achieve this using Selenium's new CDP functionality?

The CDP command to modify device metrics is Emulation.setDeviceMetricsOverride, and this command requires input of width, height, mobile device flags, and device scaling factor. These four keys are required in this scenario, but there are some optional keys.

In our Selenium tests, we could use the DevTools::send() method and use the built-in setDeviceMetricsOverride() command, but this Selenium API accepts 12 parameters - in addition to the 4 required parameters, there are 8 optional parameter. For any of these 8 optional parameters that we don't need to send, we can pass Optional.empty().

However, to simplify the process and pass only the required parameters, I'll use the original executeCdpCommand() method in the code below.

package com.devtools;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import java.util.HashMap;
import java.util.Map;

public class SetDeviceMode {
    
    

    final static String PROJECT_PATH = System.getProperty("user.dir");

    public static void main(String[] args){
    
    
        System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
        ChromeDriver driver;
        driver = new ChromeDriver();

        DevTools devTools = driver.getDevTools();
        devTools.createSession();
        Map deviceMetrics = new HashMap()
        {
    
    {
    
    
            put("width", 600);
            put("height", 1000);
            put("mobile", true);
            put("deviceScaleFactor", 50);
        }};
        driver.executeCdpCommand("Emulation.setDeviceMetricsOverride", deviceMetrics);
        driver.get("https://www.google.com");
    }
}

On line 19, I create a map containing the keys needed for this command.

Then on line 26, I call the executeCdpCommand() method, passing two parameters: the command name as "Emulation.setDeviceMetricsOverride", and the device metrics map containing the parameters.

On line 27, I open the "Google" homepage rendering the specs I provided, as shown in the image below.

picture

With a solution like Applitools Eyes, we can not only use these new Selenium commands to quickly test on different viewports, but also maintain any inconsistencies at scale. Eyes is smart enough not to report false results for small and imperceptible changes in the UI due to different browsers and viewports.

mock location

In many cases, we need to test specific location-based features, such as offers, location-based prices, etc. For this, we can use the DevTools API to mock locations.

  @Test
    public void mockLocation(){
    
    
        devTools.send(Emulation.setGeolocationOverride(
                Optional.of(48.8584),
                Optional.of(2.2945),
                Optional.of(100)));
        driver.get("https://mycurrentlocation.net/");
        try {
    
    
            Thread.sleep(30000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

Simulate network speed

Many users access web applications through handheld devices connected to Wi-Fi or cellular networks. It is very common to experience a weak network signal and therefore a slow internet connection.

In the case of a slow (2G) internet connection or intermittent network outages, it may be important to test how your application behaves under such conditions.

The CDP command to fake a network connection is Network.emulateNetworkConditions. Information about the required and optional parameters for this command can be found in the documentation.

These scenarios can be simulated by accessing the Chrome DevTools. Let's see how to do this.

package com.devtools;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.network.Network;
import org.openqa.selenium.devtools.network.model.ConnectionType;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class SetNetwork {
    
    

    final static String PROJECT_PATH = System.getProperty("user.dir");

    public static void main(String[] args){
    
    
        System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
        ChromeDriver driver;
        driver = new ChromeDriver();
        
        DevTools devTools = driver.getDevTools();
        devTools.createSession();
        devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
        devTools.send(Network.emulateNetworkConditions(
                false,
                20,
                20,
                50,
                Optional.of(ConnectionType.CELLULAR2G)
        ));
        driver.get("https://www.google.com");
    }
}

On line 21, we get the DevTools object by calling the getDevTools() method. We then call the send() method to enable the Network, and call the send() method again to pass the built-in command Network.emulateNetworkConditions() and the parameters we want to send with this command.

Finally, we open the Google homepage using simulated network conditions.

Capture HTTP requests

Using DevTools, we can capture HTTP requests made by the application and access methods, data, headers, and more.

Let's see how to capture HTTP request, URI and request method using sample code.

package com.devtools;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.network.Network;

import java.util.Optional;

public class CaptureNetworkTraffic {
    
    

    private static ChromeDriver driver;
    private static DevTools chromeDevTools;

    final static String PROJECT_PATH = System.getProperty("user.dir");

    public static void main(String[] args){
    
    
        System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
        driver = new ChromeDriver();
        chromeDevTools = driver.getDevTools();
        chromeDevTools.createSession();

        chromeDevTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
        chromeDevTools.addListener(Network.requestWillBeSent(),
                entry -> {
    
    
                    System.out.println("Request URI : " + entry.getRequest().getUrl()+"\n"
                    + " With method : "+entry.getRequest().getMethod() + "\n");
                    entry.getRequest().getMethod();
                });
        driver.get("https://www.google.com");
        chromeDevTools.send(Network.disable());
    }
}

The CDP command to start capturing network traffic is Network.enable. Information about the required and optional parameters for this command can be found in the documentation.

In our code, line 22 uses the DevTools::send() method to send the Network.enable CDP command to enable network traffic capture.

Line 23 adds a listener for all requests sent by the application. For each request captured by the application, we extract the URL with getRequest().getUrl() and the HTTP method with getRequest().getMethod().

On line 29, we open the Google homepage and print to the console the URI and HTTP method of all requests made by this page.

Once we are done capturing the request, we can send the Network.disable CDP command to stop capturing network traffic, as shown on line 30.

Intercept HTTP responses

To intercept the response we will use the Network.responseReceived event. This event is fired when an HTTP response is available, we can listen for the URL, response headers, response code, etc. To get the response body, use the Network.getResponseBody method.

 @Test
    public void validateResponse() {
    
    
        final RequestId[] requestIds = new RequestId[1];
        devTools.send(Network.enable(Optional.of(100000000), Optional.empty(), Optional.empty()));
        devTools.addListener(Network.responseReceived(), responseReceived -> {
    
    
            if (responseReceived.getResponse().getUrl().contains("api.zoomcar.com")) {
    
    
                System.out.println("URL: " + responseReceived.getResponse().getUrl());
                System.out.println("Status: " + responseReceived.getResponse().getStatus());
                System.out.println("Type: " + responseReceived.getType().toJson());
                responseReceived.getResponse().getHeaders().toJson().forEach((k, v) -> System.out.println((k + ":" + v)));
                requestIds[0] = responseReceived.getRequestId();
                System.out.println("Response Body: \n" + devTools.send(Network.getResponseBody(requestIds[0])).getBody() + "\n");
            }
        });
        driver.get("https://www.zoomcar.com/bangalore");
        driver.findElement(By.className("search")).click();
    }

access console logs

We all rely on logs for debugging and analyzing failures. When testing and working on applications with specific data or certain conditions, logs can help us debug and capture error messages, providing more insights published in the console tab of Chrome DevTools.

We can capture the console logs through our Selenium script by invoking the CDP log command as shown below.

package com.devtools;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.log.Log;

public class CaptureConsoleLogs {
    
    
    
    private static ChromeDriver driver;
    private static DevTools chromeDevTools;
    final static String PROJECT_PATH = System.getProperty("user.dir");

    public static void main(String[] args){
    
    
        System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
        driver = new ChromeDriver();
        chromeDevTools = driver.getDevTools();
        chromeDevTools.createSession();

        chromeDevTools.send(Log.enable());
        chromeDevTools.addListener(Log.entryAdded(),
                logEntry -> {
    
    
                    System.out.println("log: "+logEntry.getText());
                    System.out.println("level: "+logEntry.getLevel());
                });
        driver.get("https://testersplayground.herokuapp.com/console-5d63b2b2-3822-4a01-8197-acd8aa7e1343.php");
    }
}

In our code, line 19 uses DevTools::send() to enable console log capture.

Then, we add a listener to capture all console logs logged by the application. For each log captured by the application, we use the getText() method to extract the log text and the getLevel() method to extract the log level.

Finally, open the application and capture the console error logs issued by the application.

Capturing Performance Metrics

In today's fast-paced world, we build software iteratively at such a rate that we should also iteratively detect performance bottlenecks. Poorly performing websites and slow-loading pages leave unhappy customers.

Can we verify these metrics every build? Yes we can!

The CDP command that captures performance metrics is Performance.enable. Information about this command can be found in the documentation.

Let's see how this can be done in Selenium 4 and the Chrome DevTools API.

package com.devtools;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.performance.Performance;
import org.openqa.selenium.devtools.performance.model.Metric;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class GetMetrics {
    
    

    final static String PROJECT_PATH = System.getProperty("user.dir");

    public static void main(String[] args){
    
    
        System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
        ChromeDriver driver = new ChromeDriver();
        DevTools devTools = driver.getDevTools();
        devTools.createSession();
        devTools.send(Performance.enable());

        driver.get("https://www.google.org");

        List<Metric> metrics = devTools.send(Performance.getMetrics());
        List<String> metricNames = metrics.stream()
                .map(o -> o.getName())
                .collect(Collectors.toList());

        devTools.send(Performance.disable());

        List<String> metricsToCheck = Arrays.asList(
                "Timestamp", "Documents", "Frames", "JSEventListeners",
                "LayoutObjects", "MediaKeySessions", "Nodes",
                "Resources", "DomContentLoaded", "NavigationStart");

        metricsToCheck.forEach( metric -> System.out.println(metric +
                " is : " + metrics.get(metricNames.indexOf(metric)).getValue()));
    }
}

First, we create a session by calling the DevTools createSession() method, as shown on line 19.

Next, we enable DevTools to capture performance metrics by sending the Performance.enable() command to send(), as shown on line 20.

Once performance capture is enabled, we can open the application and send the Performance.getMetrics() command to send(). This will return a list of Metric objects, which we can stream to get the names of all the metrics captured, as shown on line 25.

We then disable performance capture by sending the Performance.disable() command to send(), as shown on line 29.

To see the metrics we are interested in, we define a list called metricsToCheck and then print the values ​​of the metrics by looping through the list.

basic authentication

In Selenium, there is no way to interact with browser popups because it can only interact with DOM elements. This poses a challenge for popups such as authentication dialogs.

We can bypass this issue by using the CDP API to handle authentication directly with DevTools. The CDP command to set extra headers for a request is Network.setExtraHTTPHeaders.

Here's how to invoke this command in Selenium 4.

package com.devtools;

import org.apache.commons.codec.binary.Base64;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.network.Network;
import org.openqa.selenium.devtools.network.model.Headers;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class SetAuthHeader {
    
    

  private static final String USERNAME = "guest";
  private static final String PASSWORD = "guest";
  final static String PROJECT_PATH = System.getProperty("user.dir");

  public static void main(String[] args){
    
    
    System.setProperty("webdriver.chrome.driver", PROJECT_PATH + "/src/main/resources/chromedriver");
    ChromeDriver driver = new ChromeDriver();

    //Create DevTools session and enable Network
    DevTools chromeDevTools = driver.getDevTools();
    chromeDevTools.createSession();
    chromeDevTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));

    //Open website
    driver.get("https://jigsaw.w3.org/HTTP/");

    //Send authorization header
    Map<String, Object> headers = new HashMap<>();
    String basicAuth ="Basic " + new String(new Base64().encode(String.format("%s:%s", USERNAME, PASSWORD).getBytes()));
    headers.put("Authorization", basicAuth);
    chromeDevTools.send(Network.setExtraHTTPHeaders(new Headers(headers)));

    //Click authentication test - this normally invokes a browser popup if unauthenticated
    driver.findElement(By.linkText("Basic Authentication test")).click();

    String loginSuccessMsg = driver.findElement(By.tagName("html")).getText();
    if(loginSuccessMsg.contains("Your browser made it!")){
    
    
      System.out.println("Login successful");
    }else{
    
    
      System.out.println("Login failed");
    }

    driver.quit();
  }
}

We start by creating a session with the DevTools object and enabling Network. This is shown on lines 25-26.

Next, we open our website, and create the authentication headers for sending.

On line 35, we send the setExtraHTTPHeaders command to send() along with the data for the headers. This part will authenticate us and allow us to bypass browser popups.

To test this functionality, we clicked on the Basic Authentication Test link. If you try this manually, you'll see a browser popup asking you to log in. But since we send the authentication header, this popup doesn't appear in our script.

Instead, we get the message "Your browser has successfully logged in!".

Summarize

Selenium has become even more powerful with the addition of the CDP API. We can now enhance our tests to capture HTTP network traffic, collect performance metrics, handle authentication, and simulate geolocation, timezone, and device mode. And anything else that might appear in Chrome DevTools!

Finally: In order to give back to the die-hard fans, I have compiled a complete software testing video learning tutorial for you. If you need it, you can get it for free【保证100%免费】
insert image description here

Software Testing Interview Documentation

We must study to find a high-paying job. The following interview questions are the latest interview materials from first-tier Internet companies such as Ali, Tencent, and Byte, and some Byte bosses have given authoritative answers. Finish this set The interview materials believe that everyone can find a satisfactory job.

insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/m0_67695717/article/details/131996083