• Selenium Video Tutorials

Selenium WebDriver - DevTools



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

什么是 Chrome DevTools?

Chrome DevTools 是一套用于 Chromium 浏览器(即 Chrome、Edge 和 Opera)的工具。它们有助于启用调试并获取有关被测应用程序的更多信息。CDP 的优势如下所示:

  • 获取控制台日志
  • 运行和调试 JavaScript
  • 模拟地理位置
  • 在文档对象模型 (DOM) 中检查 Web 元素
  • 模拟网络流量
  • 监控网络流量
  • 更新 Web 元素及其 CSS

Selenium 4 中的 Chrome DevTool API

Selenium 4 提供了新的 Chrome DevTool API,这些 API 能够实现以下功能:

  • 获取并监视网络流量。
  • 模拟地理位置以进行本地化测试。
  • 更新设备模式以检查 Web 应用程序的响应能力。

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

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

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

使用 CDP 更新设备模式

让我们举一个在其他设备中访问以下应用程序的例子。可以将应用程序配置为各种尺寸,以验证其响应能力。用于实现此目的的 CDP 命令是 **Emulation.setDeviceMetricsOverride** 命令。此命令中要传递的最小参数是高度、宽度、移动和 deviceScaleFactor。

或者,我们可以使用 **DevTools::send()** 方法,借助 **setDeviceMetricsOverride()** 方法。但是,**setDeviceMetricsOverride()** 将十二个参数作为参数。在十二个参数中,四个参数是必须的,八个参数是可选的。对于可选参数,我们必须使用 **Optional.empty()** 方法。

Selenium DevTools 1

示例

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://tutorialspoint.com/selenium/practice/selenium_automation_practice.php");
   }
}

它将显示以下输出:

Selenium DevTools 2

在上面的示例中,我们观察到屏幕尺寸变小了,其规格在代码中传递。

使用 CDP 模拟网络速度

让我们举一个检查当互联网连接处于 2G 状态时应用程序行为的例子。CDP 命令 **Network.emulateNetworkConditions** 用于实现此目的。此命令中需要传递的最小五个参数是脱机、延迟、下载吞吐量、上传吞吐量和 CONNECTIONTYPE。CONNECTIONTYPE 可以具有 3G、2G、4G、BLUETOOTH、WIFI、ETHERNET 和 NONE 等值。对于其余参数,我们必须使用 **Optional.empty()** 方法。

示例

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://tutorialspoint.com/selenium/practice/selenium_automation_practice.php");
   }
}

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

使用 CDP 模拟地理位置

让我们举一个使用 **Emulation.setGeolocationOverride** 命令模拟地理位置的例子。

示例

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/");
   }
}

它将显示以下输出:

Selenium DevTools 3

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

使用 CDP 获取 HTTP 请求

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

示例

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://tutorialspoint.com/selenium/practice/login.php");

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

它将显示以下输出:

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

along with method: GET

使用 CDP 获取控制台日志

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

示例

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://tutorialspoint.com/selenium/practice/login.php");

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

它将显示以下输出:

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

使用 CDP 获取性能指标

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

示例 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://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://tutorialspoint.com/selenium/practice/login.php");

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

它将显示以下输出:

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

示例 2

让我们举一个使用 CDP 命令 **Network.setExtraHTTPHeaders** 执行基本身份验证的例子,该命令与 send() 方法一起使用,并带有标头数据。它将有助于进行身份验证并绕过任何弹出窗口。

让我们以以下页面的示例为例,单击“基本身份验证”链接后,我们将收到一个弹出窗口,要求输入凭据。

Selenium DevTools 4

在“用户名”和“密码”字段中都传递凭据 admin,然后单击“登录”按钮继续。

Selenium DevTools 5

最后,成功身份验证后,我们将获得一个包含以下文本的页面:**恭喜!您必须拥有正确的凭据**。

Selenium DevTools 6

代码实现

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();
   }
}

它将显示以下输出:

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

Process finished with exit code 0

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

广告