• 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.selenium.dev/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 相關的其他知識,以加深您的理解並拓寬您的視野。

廣告