Selenium 简明教程

Selenium WebDriver - DevTools

Selenium Webdriver 的最新版本是 4.x 版本。Selenium 4 附带了许多改进,包括 Chrome DevTools(CDP) 上的新 API。它提供了对浏览器更精细的控制,可以对其执行测试。

The latest version of Selenium Webdriver is on the version 4.x. The Selenium 4 comes with a lot of improvements which include the new APIs on Chrome DevTools(CDP). It gives more control over the browser where testing is to be performed.

What are the Chrome DevTools?

Chrome DevTools 是一系列针对基于 Chromium 的浏览器的工具,比如 Chrome、Edge 和 Opera。它们有助于启用调试,并获取关于测试中的应用程序的其他信息。CDP 的优势如下所示:

The Chrome DevTools is a collection of tools for the Chromium based browsers namely, Chrome, Edge, and Opera. They help to enable debugging and get additional information about the application under test. The advantages of CDP are listed below −

  1. Obtain the Console Logs

  2. Run and debug JavaScript

  3. Mock Geolocations

  4. Inspect web elements in Document Object Model(DOM)

  5. Mock Network Traffic

  6. Monitor Network Traffic

  7. Update web elements along with its CSS

Chrome DevTool APIs in Selenium 4

Selenium 4 可用于新的 Chrome 开发工具 API,这些 API 具有以下所列功能:

The Selenium 4 is available with new Chrome DevTool APIs which are capable of the functionalities listed below −

  1. Obtain and keep a watch on network traffic.

  2. Mock Geolocations for localization testing.

  3. Update Device mode to check responsiveness of web application.

ChromiumDriver 类从 Selenium 4 版本引入。此类包含方法 getDevTools() 和 executeCdpCommand()。它们有助于访问 CDP。getDevTools() 方法会返回新的 DevTools 对象,它允许我们使用 send() 方法(CDP 的默认 Selenium 命令)。

The ChromiumDriver class is introduced from the Selenium 4 version. This class consists of the methods getDevTools() and executeCdpCommand(). They help to access the CDP. The getDevTools() method gives back the new DevTools object which allows us to use the send() method (the default Selenium commands for CDP).

这些命令主要是帮助调用 CDP 函数的封装方法。另一方面,executeCdpCommand() 方法可帮助在参数的帮助下运行 CDP 方法。它不依赖于任何封装 API。

These commands are primarily the wrapper methods which help to invoke the CDP functions. On the other hand, the executeCdpCommand() method helps to run the CDP methods with the help of the parameters. It is devoid of any wrapper APIs.

ChromeDriver 和 EdgeDriver 类是从 ChromiumDriver 类继承的。因此,这些驱动程序也可以访问 Selenium CDP API。

The ChromeDriver and EdgeDriver classes are inherited from the ChromiumDriver class. Thus the Selenium CDP APIs can be accessed from these drivers too.

Update Device Mode using CDP

让我们举一个示例,其中我们在其他设备中访问下面的应用程序。可以将应用程序配置为多种尺寸以验证其响应能力。为此使用的 CDP 命令是 Emulation.setDeviceMetricsOverride 命令。此命令中要传递的最小参数是 height、width、mobile 和 deviceScaleFactor。

Let us take an example where we would access the below application in other devices. The application can be configured to various dimensions to verify its responsiveness. The CDP command used to achieve this, Emulation.setDeviceMetricsOverride command. The minimum parameters to be passed in this command are height, width, mobile and deviceScaleFactor.

作为替代方法,我们可以借助 setDeviceMetricsOverride() 方法来使用 DevTools::send() 方法。但是, setDeviceMetricsOverride() 使用 12 个参数。12 个参数中,4 个是必填,8 个是可选的。对于可选参数,我们必须使用 Optional.empty() 方法。

As an alternative we can use DevTools::send() method by taking the help of the setDeviceMetricsOverride() method. However, the setDeviceMetricsOverride() takes twelve arguments as parameters. Out of twelve, four parameters are mandatory, and eight are optional. For the options ones we have to use the method Optional.empty().

selenium devtools 1

Example

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chromium.HasCdp;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class UpdateDeviceCDP {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // device configurations
      Map dM = new HashMap(){
         {
            put("width", 500);
            put("height", 600);
            put("mobile", true);
            put("deviceScaleFactor", 50);
         }
      };
      ((HasCdp) driver).executeCdpCommand("Emulation.setDeviceMetricsOverride", dM);

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/selenium_automation_practice.php");
   }
}

它将显示以下输出 −

It will show the following output −

selenium devtools 2

在上面的示例中,我们可以看到屏幕尺寸已经变小,具有在代码中传递的规格。

In the above example, we observe that the screen size has become smaller having the specifications passed in the code.

Simulate Network Speed using CDP

让我们举一个示例,其中当互联网连接为 2G 时,我们将检查应用程序的行为。CDP 命令 Network.emulateNetworkConditions 用于实现此目的。此命令中需要传递的至少 5 个参数是 offline、latency、download throughput、upload throughput 和 CONNECTIONTYPE。CONNECTIONTYPE 可以具有如下值:3G、2G、4G、BLUETOOTH、WIFI、ETHERNET 和 NONE。对于其他参数,我们必须使用 Optional.empty() 方法。

Let us take an example where we would check the application behavior when the internet connection is on the 2G. The CDP command Network.emulateNetworkConditions is used to achieve this. The minimum five parameters that need to be passed in this command are offline, latency, download throughput, upload throughput, and CONNECTIONTYPE. The CONNECTIONTYPE can have the values as 3G, 2G, 4G, BLUETOOTH, WIFI, ETHERNET, and NONE. For the rest of the parameters we have to use the method Optional.empty().

Example

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.network.model.ConnectionType;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.devtools.v124.network.Network;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class UpdateNetworkCDP {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // network 2G configurations
      d.send(Network.enable(Optional.empty(),Optional.empty(), Optional.empty()));
      d.send(Network.emulateNetworkConditions(
         false,
         50,
         30,
         40,
         Optional.of(ConnectionType.CELLULAR2G),
         Optional.empty(),
         Optional.empty(),
         Optional.empty()
      ));

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/selenium_automation_practice.php");
   }
}

在上面的示例中,我们已经使用模拟的 2G 网络连接打开了应用程序。

In the above example, we have opened the application with a simulated network connection of 2G.

Emulate Geolocations using CDP

让我们举一个示例,其中我们将使用 Emulation.setGeolocationOverride 命令来模拟地理位置。

Let us take an example where we would emulate a geolocation using the Emulation.setGeolocationOverride command.

Example

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.emulation.Emulation;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class UpdateGeolocations {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // update geolocations latitude and longitude
      d.send(Emulation.setGeolocationOverride(
         Optional.of(48.78232),
         Optional.of(9.17702),
         Optional.of(80)
      ));

      // open application url
      driver.get("https://where-am-i.org/");
   }
}

它将显示以下输出 −

It will show the following output −

selenium devtools 3

在上面的示例中,我们已将地理位置覆写为德国。

In the above example, we had overwritten the geolocations to Germany.

Obtain HTTP Requests using CDP

我们举一个示例,在该示例中,将在启动应用程序的同时获取 HTTP 请求及其响应、数据、标题等。要开始捕获网络流量,我们将设置与 send() 方法一起使用的 Network.enable 。此外,我们将分别使用 getRequest().getUrl()getRequest().getMethod() 方法获取 URL 和方法名称。

Let us take an example where we would obtain the HTTP requests while an application is launched along with its response, data, headers etc. To begin capturing network traffic, we would set the Network.enable which is used along with the send() method. Also, we would get the URL and the method name using the getRequest().getUrl() and getRequest().getMethod() methods respectively.

Example

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.network.Network;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class ObtainHttpReq {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // get network traffic
      d.send(Network.enable(Optional.empty(),
      Optional.empty(), Optional.empty()));
      d.addListener(Network.requestWillBeSent(),
      e -> {
         System.out.println("Get Request URI: " + e.getRequest().getUrl()+ "\n"
            + "along with method: "+ e.getRequest().getMethod() + "\n");
         e.getRequest().getMethod();
      });

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/login.php");

      // quit browser
      driver.quit();
   }
}

它将显示以下输出 −

It will show the following output −

Get Request URI:
https://www.tutorialspoint.com/selenium/practice/login.php

along with method: GET

Obtain Console Logs using CDP

我们举一个示例,在该示例中,将获取控制台日志。这有助于调试和对测试失败进行根本原因分析。要开始捕获控制台日志,我们将设置与 send() 方法一起使用的 Log.enable 。此外,我们将分别使用 getText()getLevel() 方法获取日志文本和日志级别。

Let us take an example where we would obtain the console logs. This helps for debugging, and root cause analysis of test failures. To begin capturing the console logs, we would set the Log.enable which is used along with the send() method. Also, we would get the log text and the log level using the getText() and getLevel() methods respectively.

Example

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.log.Log;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.concurrent.TimeUnit;

public class LogLevelCdp {
   public static void main(String[] args) throws InterruptedException {

      // Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // enable logging
      d.send(Log.enable());

      // get log levels and text
      d.addListener(Log.entryAdded(),
         logEntry -> {
            System.out.println("Log text: "+logEntry.getText());
            System.out.println("Log level: "+logEntry.getLevel());
         });

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/login.php");

      // quit browser
      driver.quit();
   }
}

它将显示以下输出 −

It will show the following output −

Log text: [DOM] Input elements should have autocomplete attributes
(suggested: "current-password"):
(More info: https://www.chromium.org/developers/design-documents/create-amazing-password-forms) %o

Log level: verbose

Obtain Performance Metrics using CDP

我们举一个示例,在该示例中,将获取应用程序的性能指标。要开始捕获指标,我们将设置与 send() 方法一起使用的 Performance.enable 。此外,我们将使用 Performance.getMetrics() 方法获取所有指标。

Let us take an example where we would obtain the performance metrics of an application. To begin capturing the metrics, we would set the Performance.enable which is used along with the send() method. Also, we would get all the metrics using the Performance.getMetrics() method.

Example 1

package org.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.performance.Performance;
import org.openqa.selenium.devtools.v124.performance.model.Metric;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class PerformanceMonitoringCdp {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();

      // enable performance monitoring
      d.send(Performance.enable(Optional.empty()));

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/text-box.php");

      // get performance
      List<Metric> m = d.send(Performance.getMetrics());
      List<String> mN = m.stream()
         .map(o -> o.getName())
         .collect(Collectors.toList());

      d.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 : " + m.get(mN.indexOf(metric)).getValue()));

      // open application url
      driver.get("https://www.tutorialspoint.com/selenium/practice/login.php");

      // quit browser
      driver.quit();
   }
}

它将显示以下输出 −

It will show the following output −

Timestamp is : 15204.440213
Documents is : 7
Frames is : 4
JSEventListeners is : 30
LayoutObjects is : 170
MediaKeySessions is : 0
Nodes is : 528
Resources is : 10
DomContentLoaded is : 15204.419683
NavigationStart is : 15203.25931

Process finished with exit code 0

Example 2

我们举一个示例,在该示例中,将使用与 send() 方法一起使用的 CDP 命令 Network.setExtraHTTPHeaders 和标题数据执行基本身份验证。这将有助于验证身份并绕过任何弹出窗口。

Let us take an example where we would perform basic authentication using the CDP command Network.setExtraHTTPHeaders which is used along with the send() method, and with the header data. It would help to authenticate and bypass any popup.

让我们举下面的页面的示例,单击“基本身份验证”链接后,我们将获得一个弹出窗口,要求提供凭据。

Let us take the example of the below page, on clicking the Basic Auth link, we would get a popup, asking for credentials.

selenium devtools 4

凭据 admin 在“用户名”和“密码”字段中均已传递,然后单击 Sign In 按钮以继续。

The credentials admin is passed in both the fields Username and Password, then the Sign In button is clicked to proceed.

selenium devtools 5

最后,在成功身份验证后,我们将获得一个带有文本 - Congratulations! You must have the proper credentials 的页面。

Finally, after successfully authentication, we would get a page with the text - Congratulations! You must have the proper credentials.

selenium devtools 6

代码实现

Code Implementation

package org.example;

import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v124.network.Network;
import org.openqa.selenium.devtools.v124.network.model.Headers;
import org.openqa.selenium.edge.EdgeDriver;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class BasicAuthCdp {
   public static void main(String[] args) throws InterruptedException {

      //Initiate the Webdriver
      WebDriver driver = new EdgeDriver();

      //adding implicit wait of 15 secs
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);

      // instance of DevTools
      DevTools d = ((HasDevTools) driver).getDevTools();

      // create a session
      d.createSession();
      d.send(Network.enable(Optional.empty(),Optional.empty(), Optional.empty()));

      String encodedAuth = Base64.getEncoder().encodeToString("admin:admin".getBytes());
      Map<String, Object> headers = ImmutableMap.of("Authorization", "Basic " + encodedAuth);

      d.send(Network.setExtraHTTPHeaders(new Headers(headers)));

      // open application
      driver.get("https://the-internet.herokuapp.com/basic_auth");

      WebElement e = driver.findElement(By.tagName("p"));
      System.out.println("Text is: " + e.getText());

      // quit browser
      driver.quit();
   }
}

它将显示以下输出 −

It will show the following output −

Text is: Congratulations! You must have the proper credentials.

Process finished with exit code 0

在上面的示例中,我们在标头中传递了身份验证,因此在触发测试时没有遇到弹出窗口。

In the above example, we had passed the authentication in the header, hence had not encountered the popup while triggering the test.