• Selenium Video Tutorials

Selenium Grid - 自定义节点



Selenium Grid 4 的最新版本是在没有利用 Selenium Grid 旧版本代码库的情况下开发的。此最新版本的 Selenium Grid 4 版本具有一些高级功能,例如自定义节点。

最新版本的 Selenium Grid 允许在三种不同的 Selenium Grid 模式下触发测试执行。它们被称为独立模式、集线器和节点模式以及分布式模式。

Selenium Grid 中的节点自定义

在使用 Selenium Grid 在不同浏览器及其平台、设备上执行并行测试或跨浏览器测试时,我们可能需要根据需求自定义节点。

在执行会话开始之前,我们可能需要添加一些先决条件步骤,或者在会话完成后执行任何整理活动。要执行节点自定义,需要执行以下步骤:

  • 设计一个类,该类将扩展 -

org.openqa.selenium.grid.node.Node.
  • 在新建的类中追加一个静态方法(工厂方法),该方法应具有以下签名:public static Node created(Config config)。此处,Node 与 org.openqa.selenium.grid.node.Node 类型相同,Config 与 - 类型相同。

org.openqa.selenium.grid.config.Config.
  • 在工厂方法内部,应添加新类的实现逻辑。

  • 要将新开发的实现合并到集线器中,请启动节点并将前一个的完全限定类名发送到参数:–node-implementation。

使用 Uber Jar 进行节点自定义

使用 uber jar 实现节点自定义的步骤如下所列:

  • 在系统中安装 Java(版本高于 8),并使用命令检查它是否存在:java -version。如果安装已成功完成,则将显示已安装的 Java 版本。

  • 在系统中安装 Maven,并使用命令检查它是否存在:mvn -version。如果安装已成功完成,则将显示已安装的 Maven 版本。

  • 安装任何 IDE,如 Eclipse、IntelliJ 等。

  • 从链接将 Selenium Grid 依赖项添加到 pom.xml 中:

    https://mvnrepository.com/artifact/.

  • 从链接将 Apache Maven Shade Plugin 添加到 pom.xml 中:

    https://mvnrepository.com/artifact/.

  • 将自定义节点附加到当前项目。

  • 构建 uber jar 以使用命令启动节点:java -jar。

  • 使用以下命令启动节点:

java -jar custom_node-server.jar node \
--node-implementation org.seleniumhq.samples.DecoratedLoggingNode

使用普通 Jar 进行节点自定义

使用普通 jar 实现节点自定义的步骤如下所列:

  • 在系统中安装 Java(版本高于 8),并使用命令检查它是否存在:java -version。如果安装已成功完成,则将显示已安装的 Java 版本。

  • 在系统中安装 Maven,并使用命令检查它是否存在:mvn -version。如果安装已成功完成,则将显示已安装的 Maven 版本。

  • 安装任何 IDE,如 Eclipse、IntelliJ 等。

  • 从链接将 Selenium Grid 依赖项添加到 pom.xml 中:https://mvnrepository.com/artifact/

  • 从链接将 Apache Maven Shade Plugin 添加到 pom.xml 中:https://maven.apache.org/plugins/

  • 将自定义节点附加到当前项目。

  • 构建 uber jar 以使用命令启动节点:java -jar。

  • 使用以下命令启动节点:

java -jar selenium-server-4.6.0.jar \
--ext custom_node-1.0-SNAPSHOT.jar node \
--node-implementation org.seleniumhq.samples.DecoratedLoggingNode

DecoratedLoggingNode.java 中的代码实现

package org.seleniumhq.samples;

import java.io.IOException;
import java.net.URI;
import java.util.UUID;
import java.util.function.Supplier;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.NoSuchSessionException;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.grid.config.Config;
import org.openqa.selenium.grid.data.CreateSessionRequest;
import org.openqa.selenium.grid.data.CreateSessionResponse;
import org.openqa.selenium.grid.data.NodeId;
import org.openqa.selenium.grid.data.NodeStatus;
import org.openqa.selenium.grid.data.Session;
import org.openqa.selenium.grid.log.LoggingOptions;
import org.openqa.selenium.grid.node.HealthCheck;
import org.openqa.selenium.grid.node.Node;
import org.openqa.selenium.grid.node.local.LocalNodeFactory;
import org.openqa.selenium.grid.security.Secret;
import org.openqa.selenium.grid.security.SecretOptions;
import org.openqa.selenium.grid.server.BaseServerOptions;
import org.openqa.selenium.internal.Either;
import org.openqa.selenium.io.TemporaryFilesystem;
import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.tracing.Tracer;

public class DecoratedLoggingNode extends Node {
   private Node node;

   protected DecoratedLoggingNode(Tracer tracer, NodeId nodeId, URI uri, Secret registrationSecret) {
      super(tracer, nodeId, uri, registrationSecret);
   }

   public static Node create(Config config) {
      LoggingOptions loggingOptions = new LoggingOptions(config);
      BaseServerOptions serverOptions = new BaseServerOptions(config);
      URI uri = serverOptions.getExternalUri();
      SecretOptions secretOptions = new SecretOptions(config);

      // Refer to the foot notes for additional context on this line.
      Node node = LocalNodeFactory.create(config);
      DecoratedLoggingNode wrapper = new DecoratedLoggingNode(loggingOptions.getTracer(),
         node.getId(),
         uri,
         secretOptions.getRegistrationSecret());
      wrapper.node = node;
      return wrapper;
   }

   @Override
   public Either<WebDriverException, CreateSessionResponse> newSession(
   CreateSessionRequest sessionRequest) {
      return perform(() -> node.newSession(sessionRequest), "newSession");
   }

   @Override
   public HttpResponse executeWebDriverCommand(HttpRequest req) {
      return perform(() -> node.executeWebDriverCommand(req), "executeWebDriverCommand");
   }

   @Override
   public Session getSession(SessionId id) throws NoSuchSessionException {
      return perform(() -> node.getSession(id), "getSession");
   }

   @Override
   public HttpResponse uploadFile(HttpRequest req, SessionId id) {
      return perform(() -> node.uploadFile(req, id), "uploadFile");
   }

   @Override
   public HttpResponse downloadFile(HttpRequest req, SessionId id) {
      return perform(() -> node.downloadFile(req, id), "downloadFile");
   }

   @Override
   public TemporaryFilesystem getDownloadsFilesystem(UUID uuid) {
      return perform(() -> {
         try {
            return node.getDownloadsFilesystem(uuid);
         } catch (IOException e) {
            throw new RuntimeException(e);
         }
      }, "downloadsFilesystem");
   }

   @Override
   public TemporaryFilesystem getUploadsFilesystem(SessionId id) throws IOException {
      return perform(() -> {
         try {
            return node.getUploadsFilesystem(id);
         } catch (IOException e) {
            throw new RuntimeException(e);
         }
      }, "uploadsFilesystem");
   }

   @Override
   public void stop(SessionId id) throws NoSuchSessionException {
      perform(() -> node.stop(id), "stop");
   }

   @Override
   public boolean isSessionOwner(SessionId id) {
      return perform(() -> node.isSessionOwner(id), "isSessionOwner");
   }

   @Override
   public boolean isSupporting(Capabilities capabilities) {
      return perform(() -> node.isSupporting(capabilities), "isSupporting");
   }

   @Override
   public NodeStatus getStatus() {
      return perform(() -> node.getStatus(), "getStatus");
   }

   @Override
   public HealthCheck getHealthCheck() {
      return perform(() -> node.getHealthCheck(), "getHealthCheck");
   }

   @Override
   public void drain() {
      perform(() -> node.drain(), "drain");
   }

   @Override
   public boolean isReady() {
      return perform(() -> node.isReady(), "isReady");
   }

   private void perform(Runnable function, String operation) {
      try {
         System.err.printf("[COMMENTATOR] Before %s()%n", operation);
         function.run();
      } finally {
         System.err.printf("[COMMENTATOR] After %s()%n", operation);
      }
   }

   private <T> T perform(Supplier<T> function, String operation) {
      try {
         System.err.printf("[COMMENTATOR] Before %s()%n", operation);
         return function.get();
      } finally {
         System.err.printf("[COMMENTATOR] After %s()%n", operation);
      }
   }
}

DecoratedLoggingNode.java 的源代码:https://www.seleniumcn.cn/documentation/

在上述实现中,以下代码:

Node node = LocalNodeFactory.create(config) 用于专门生成 LocalNode。org.openqa.selenium.grid.node.Node 有两种面向用户的实现。这些是创建自定义节点和收集有关节点信息的绝佳起点。

  • org.openqa.selenium.grid.node.local.LocalNode - 用于指向长时间运行的节点,并且是启动节点时获取的默认逻辑。可以通过调用 LocalNodeFactory.create(config) 生成它。此处,LocalNodeFactory 是 org.openqa.selenium.grid.node.local 的一部分,Config 是 org.openqa.selenium.grid.config 的一部分。

  • org.openqa.selenium.grid.node.k8s.OneShotNode - 这是一个重要的参考逻辑,其中节点在一次测试会话后正确关闭自身。此类对任何预先存在的 Maven 工件不可用。可以通过调用 OneShotNode.create(config) 生成它。此处,OneShotNode 是 org.openqa.selenium.grid.node.k8s 的一部分,Config 是 org.openqa.selenium.grid.config 的一部分。

这结束了我们关于 Selenium Grid - 自定义节点教程的全面介绍。我们从描述如何在 Selenium Grid 中执行节点自定义、使用 uber jar 进行节点自定义以及在 Selenium Grid 中使用普通 jar 进行节点自定义开始。

这使您深入了解了 Selenium Grid 自定义节点。明智的做法是不断练习您学到的知识并探索与 Selenium 相关的其他知识,以加深您的理解并拓宽您的视野。

广告