Selenium 简明教程

Selenium Grid - Customizing a Node

最新版本的 Selenium Grid 4 是在不利用较旧版本的 Selenium Grid 的代码库的情况下开发的。最新版本的 Selenium Grid 4 版本拥有 Node 自定化等一些高级功能。

The latest version of Selenium Grid 4, is developed without leveraging the code base of the older versions of the Selenium Grid. This latest version of Selenium Grid 4 version has some advanced features like the customizing a Node.

最新版本的 Selenium Grid 允许在三种不同的 Selenium Grid 模式下触发测试执行。它们被称为 Standalone、Hub 和 Nodes 以及 Distributed。

The latest version of Selenium Grid allows test execution to be triggered in three different Selenium Grid modes. They are known as the Standalone, Hub and Nodes, and the Distributed.

Node Customization in Selenium Grid

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

While using the Selenium Grid to perform parallel testing or cross browser testing across different browsers and its platforms, devices, we may require to customize the Node as per requirements.

在执行会话开始前,可能需要添加一些先决步骤或在会话完成后进行任何整理活动。要自定义 Node,请执行以下步骤 −

We may need to add some prerequisite steps prior to starting of the execution sessions or any tidying up activities post the sessions completed. To perform a Node customization the below steps are to be performed −

  1. Design a class which will extend the −

org.openqa.selenium.grid.node.Node.
  1. Append a static method(a factory method) to the newly designed class which should have the signature: public static Node created(Config config). Here, the Node has the same type as org.openqa.selenium.grid.node.Node and Config has the same type as −

org.openqa.selenium.grid.config.Config.
  1. Inside the factory method, the implementation logic of the new class should be added.

  2. To incorporate the newly developed implementation inside the hub, begin the node and send in the completely qualified class name of the previous to the argument: –node-implementation.

Node Customization Using the Uber Jar

使用 uber jar 实现 node 自定化的步骤如下 −

The steps to implement node customization using the uber jar is listed below −

  1. Install Java(version above 8) in the system and check if it is present with the command: java -version. The java version installed will be visible if installation has been completed successfully.

  2. Install maven in the system and check if it is present with the command: mvn -version. The maven version installed will be visible if installation has been completed successfully.

  3. Install any IDE like Eclipse, IntelliJ, and so on.

  4. Add the Selenium Grid dependencies to the pom.xml from the link − https://mvnrepository.com/artifact/.

  5. Add the Apache Maven Shade Plugin to the pom.xml from the link − https://mvnrepository.com/artifact/.

  6. Append the customized node to the current project.

  7. Build the uber jar to kick off the Node with the help of the command: java -jar.

  8. Begin the Node using the below command −

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

Node Customization Using a Normal Jar

下面列出使用普通 JAR 实现节点自定义的步骤 −

The steps to implement node customization using a normal jar is listed below −

  1. Install Java(version above 8) in the system and check if it is present with the command: java -version. The java version installed will be visible if installation has been completed successfully.

  2. Install maven in the system and check if it is present with the command: mvn -version. The maven version installed will be visible if installation has been completed successfully.

  3. Install any IDE like Eclipse, IntelliJ, and so on.

  4. Add the Selenium Grid dependencies to the pom.xml from the link − https://mvnrepository.com/artifact/.

  5. Add the Apache Maven Shade Plugin to the pom.xml from the link − https://maven.apache.org/plugins/.

  6. Append the customized node to the current project.

  7. Build the uber jar to kick off the Node with the help of the command: java -jar.

  8. Begin the Node using the below command −

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

DecoratedLoggingNode.java 中实现代码。

Code Implementation in 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/

Source for DecoratedLoggingNode.java − https://www.selenium.dev/documentation/.

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

In the above implementation, the below code −

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

Node node = LocalNodeFactory.create(config) is used to generate the LocalNode specifically. There are two types of user facing implementations of the org.openqa.selenium.grid.node.Node. These are excellent beginning areas to create a custom Node and to gather information on a Node.

  1. org.openqa.selenium.grid.node.local.LocalNode − This is used to point to a Node which is running for a long time and is the default logic that is fetched while a Node is started. It can be generated by calling the LocalNodeFactory.create(config). Here, the LocalNodeFactory is a part of the org.openqa.selenium.grid.node.local and Config is a part of the org.openqa.selenium.grid.config.

  2. org.openqa.selenium.grid.node.k8s.OneShotNode − This is an important reference logic where the Node shuts itself down properly post one test session. This class is unavailable to any pre-existing maven artifact. It can be generated by calling the OneShotNode.create(config). Here, the OneShotNode is a part of the org.openqa.selenium.grid.node.k8s and Config is a part of the org.openqa.selenium.grid.config.

这样就完成了我们关于使用 Selenium Grid 自定义节点教程的全面讲解。我们从如何使用 Selenium Grid 执行节点自定义、使用 uber JAR 的节点自定义以及在 Selenium Grid 中使用普通 JAR 的节点自定义开始介绍。

This concludes our comprehensive take on the tutorial on Selenium Grid - Customizing a Node. We’ve started with describing how to perform a Node customization in Selenium Grid, Node Customization using the uber jar, and Node Customization using a normal jar in Selenium Grid.

这使你深入了解 Selenium Grid 自定义节点。明智的做法是不断实践你所学到的知识,探索与 Selenium 相关的内容,以便加深你的理解并拓宽你的视野。

This equips you with in-depth knowledge of the Selenium Grid Customizing a Node. It is wise to keep practicing what you’ve learned and exploring others relevant to Selenium to deepen your understanding and expand your horizons.