Selenium 简明教程
Selenium Grid - Customizing a Node
最新版本的 Selenium Grid 4 是在不利用较旧版本的 Selenium Grid 的代码库的情况下开发的。最新版本的 Selenium Grid 4 版本拥有 Node 自定化等一些高级功能。
最新版本的 Selenium Grid 允许在三种不同的 Selenium Grid 模式下触发测试执行。它们被称为 Standalone、Hub 和 Nodes 以及 Distributed。
Node Customization in Selenium Grid
在使用 Selenium Grid 在不同的浏览器及其实平台、设备上执行并行测试或跨浏览器测试时,可能需要根据实际需要自定义 Node。
在执行会话开始前,可能需要添加一些先决步骤或在会话完成后进行任何整理活动。要自定义 Node,请执行以下步骤 −
-
设计一个将扩展 − 的类
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.
-
在工厂方法内部,应添加新类的实现逻辑。
-
要将新开发的实现纳入 hub,请启动 node 并向 argumentos:-node-implementation 中的上一个类发送完全限定类名称。
Node Customization Using the Uber Jar
使用 uber jar 实现 node 自定化的步骤如下 −
-
在系统中安装 Java(版本高于 8),并使用以下命令检查它是否存在:java -version。如果已成功完成安装,将显示已安装的 java 版本。
-
在系统中安装 maven 并使用以下命令检查它是否存在:mvn -version。如果已成功完成安装,将显示已安装的 maven 版本。
-
安装任何一个 IDE,例如 Eclipse、IntelliJ 等等。
-
从链接 https://mvnrepository.com/artifact/ 向 pom.xml 中添加 Selenium Grid 依赖项。
-
从链接 https://mvnrepository.com/artifact/ 向 pom.xml 中添加 Apache Maven Shade Plugin。
-
向当前项目中追加自定义的 node。
-
借助以下命令构建 uber jar 以启动 Node:java -jar。
-
使用以下命令启动节点 −
java -jar custom_node-server.jar node \
--node-implementation org.seleniumhq.samples.DecoratedLoggingNode
Node Customization Using a Normal 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/ 。
-
向当前项目中追加自定义的 node。
-
借助以下命令构建 uber jar 以启动 Node: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 相关的内容,以便加深你的理解并拓宽你的视野。