Skip to content

三、WebDriver ​

1、浏览器 ​

1、获取标题 ​

java
driver.getTitle();

2、获取当前 URL ​

java
driver.getCurrentUrl();

3、导航 ​

1、打开网站 ​

java
// 简便的方法
driver.get("https://selenium.dev");

// 更长的方法
driver.navigate().to("https://selenium.dev");

2、后退 ​

按下浏览器的后退按钮

java
driver.navigate().back();

3、前进 ​

按下浏览器的前进键

java
driver.navigate().forward();

4、刷新 ​

刷新当前页面

java
driver.navigate().refresh();

4、Cookies ​

这个方法常常用于将cookie添加到当前访问的上下文中.

首先, 您需要位于有效Cookie的域上. 如果您在开始与网站进行交互之前尝试预设cookie, 并且您的首页很大或需要一段时间才能加载完毕, 则可以选择在网站上找到一个较小的页面 (通常404页很小, 例如 http://example.com/some404page)

java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class addCookie {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        try {
            driver.get("http://www.example.com");

            // Adds the cookie into current browser context
            driver.manage().addCookie(new Cookie("key", "value"));
        } finally {
            driver.quit();
        }
    }
}

此方法返回与cookie名称匹配的序列化cookie数据中所有关联的cookie.

java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class getCookieNamed {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        try {
            driver.get("http://www.example.com");
            driver.manage().addCookie(new Cookie("foo", "bar"));

            // Get cookie details with named cookie 'foo'
            Cookie cookie1 = driver.manage().getCookieNamed("foo");
            System.out.println(cookie1);
        } finally {
            driver.quit();
        }
    }
}

3、获取全部 Cookies ​

此方法会针对当前访问上下文返回“成功的序列化cookie数据”. 如果浏览器不再可用, 则返回错误.

java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.Set;

public class getAllCookies {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        try {
            driver.get("http://www.example.com");
            // Add few cookies
            driver.manage().addCookie(new Cookie("test1", "cookie1"));
            driver.manage().addCookie(new Cookie("test2", "cookie2"));

            // Get All available cookies
            Set<Cookie> cookies = driver.manage().getCookies();
            System.out.println(cookies);
        } finally {
            driver.quit();
        }
    }
}

此方法删除当前访问上下文的所有cookie.

java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class deleteAllCookies {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        try {
            driver.get("http://www.example.com");
            driver.manage().addCookie(new Cookie("test1", "cookie1"));
            driver.manage().addCookie(new Cookie("test2", "cookie2"));

            // deletes all cookies
            driver.manage().deleteAllCookies();
        } finally {
            driver.quit();
        }
    }
}

5、Frames ​

与iFrames和frames一起工作

框架是一种现在已被弃用的方法,用于从同一域中的多个文档构建站点布局。除非你使用的是 HTML5 之前的 webapp,否则你不太可能与他们合作。内嵌框架允许插入来自完全不同领域的文档,并且仍然经常使用。

如果您需要使用框架或 iframe, WebDriver 允许您以相同的方式使用它们。考虑 iframe 中的一个按钮。 如果我们使用浏览器开发工具检查元素,我们可能会看到以下内容

html
<div id="modal">
  <iframe id="buttonframe"name="myframe"src="https://seleniumhq.github.io">
   <button>Click here</button>
 </iframe>
</div>

如果不是 iframe,我们可能会使用如下方式点击按钮:

java
// 这不会工作
driver.findElement(By.tagName("button")).click();

但是,如果 iframe 之外没有按钮,那么您可能会得到一个 no such element 无此元素 的错误。 这是因为 Selenium 只知道顶层文档中的元素。为了与按钮进行交互,我们需要首先切换到框架, 这与切换窗口的方式类似。WebDriver 提供了三种切换到帧的方法。

1、使用 WebElement ​

使用 WebElement 进行切换是最灵活的选择。您可以使用首选的选择器找到框架并切换到它。

java
// 存储网页元素
WebElement iframe = driver.findElement(By.cssSelector("#modal>iframe"));

// 切换到 frame
driver.switchTo().frame(iframe);

// 现在可以点击按钮
driver.findElement(By.tagName("button")).click();

2、使用 name 或 id ​

如果您的 frame 或 iframe 具有 id 或 name 属性,则可以使用该属性。如果名称或 id 在页面上不是唯一的, 那么将切换到找到的第一个。

java
// 使用 ID
driver.switchTo().frame("buttonframe");

// 或者使用 name 代替
driver.switchTo().frame("myframe");

// 现在可以点击按钮
driver.findElement(By.tagName("button")).click();

3、使用索引 ​

还可以使用frame的索引, 例如可以使用JavaScript中的 window.frames 进行查询.

java
// 切换到第 2 个框架
driver.switchTo().frame(1);

4、离开框架 ​

离开 iframe 或 frameset,切换回默认内容,如下所示:

java
// 回到顶层
driver.switchTo().defaultContent();

6、窗口和标签页 ​

WebDriver 没有区分窗口和标签页。如果你的站点打开了一个新标签页或窗口,Selenium 将允许您使用窗口句柄来处理它。 每个窗口都有一个唯一的标识符,该标识符在单个会话中保持持久性。你可以使用以下方法获得当前窗口的窗口句柄

java
driver.getWindowHandle();

1、切换窗口或标签页 ​

单击在 <a href=“https://seleniumhq.github.io"target="_blank”>新窗口 中打开链接, 则屏幕会聚焦在新窗口或新标签页上,但 WebDriver 不知道操作系统认为哪个窗口是活动的。 要使用新窗口,您需要切换到它。 如果只有两个选项卡或窗口被打开,并且你知道从哪个窗口开始, 则你可以遍历 WebDriver, 通过排除法可以看到两个窗口或选项卡,然后切换到你需要的窗口或选项卡。

不过,Selenium 4 提供了一个新的 api NewWindow 它创建一个新选项卡 (或) 新窗口并自动切换到它。

java
// 存储原始窗口的 ID
String originalWindow = driver.getWindowHandle();

// 检查一下,我们还没有打开其他的窗口
assert driver.getWindowHandles().size() == 1;

// 点击在新窗口中打开的链接
driver.findElement(By.linkText("new window")).click();

// 等待新窗口或标签页
wait.until(numberOfWindowsToBe(2));

// 循环执行,直到找到一个新的窗口句柄
for (String windowHandle : driver.getWindowHandles()) {
  if(!originalWindow.contentEquals(windowHandle)) {
    driver.switchTo().window(windowHandle);
			break;
		}
}

// 等待新标签完成加载内容
wait.until(titleIs("Selenium documentation"));

2、创建新窗口(或)新标签页并且切换 ​

创建一个新窗口 (或) 标签页,屏幕焦点将聚焦在新窗口或标签在上。您不需要切换到新窗口 (或) 标签页。如果除了新窗口之外, 您打开了两个以上的窗口 (或) 标签页,您可以通过遍历 WebDriver 看到两个窗口或选项卡,并切换到非原始窗口。

注意: 该特性适用于 Selenium 4 及其后续版本。

java
// 打开新标签页并切换到新标签页
driver.switchTo().newWindow(WindowType.TAB);

// 打开一个新窗口并切换到新窗口
driver.switchTo().newWindow(WindowType.WINDOW);

3、关闭窗口或标签页 ​

当你完成了一个窗口或标签页的工作时,_并且_它不是浏览器中最后一个打开的窗口或标签页时,你应该关闭它并切换回你之前使用的窗口。 假设您遵循了前一节中的代码示例,您将把前一个窗口句柄存储在一个变量中。把这些放在一起,你会得到:

java
//关闭标签页或窗口
driver.close();

//切回到之前的标签页或窗口
driver.switchTo().window(originalWindow);

4、在会话结束时退出浏览器 ​

当你完成了浏览器会话,你应该调用 quit 退出,而不是 close 关闭:

java
driver.quit();
  • 退出将会
    • 关闭所有与 WebDriver 会话相关的窗口和选项卡
    • 结束浏览器进程
    • 结束后台驱动进程
    • 通知 Selenium Grid 浏览器不再使用,以便可以由另一个会话使用它(如果您正在使用 Selenium Grid)

调用 quit() 失败将留下额外的后台进程和端口运行在机器上,这可能在以后导致一些问题。

有的测试框架提供了一些方法和注释,您可以在测试结束时放入 teardown() 方法中。

java
/**
  * 使用 JUnit 的例子
  * https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/AfterAll.html
  */
  @AfterAll
  public static void tearDown() {driver.quit();
  }

如果不在测试上下文中运行 WebDriver,您可以考虑使用 try / finally,这是大多数语言都提供的, 这样一个异常处理仍然可以清理 WebDriver 会话。

java
try {
  //WebDriver 代码…} finally {driver.quit();
}

5、获取窗口大小 ​

获取浏览器窗口的大小(以像素为单位)。

java
// 分别获取每个尺寸
int width = driver.manage().window().getSize().getWidth();
int height = driver.manage().window().getSize().getHeight();

// 或者存储尺寸并在以后查询它们
Dimension size = driver.manage().window().getSize();
int width1 = size.getWidth();
int height1 = size.getHeight();

6、设置窗口大小 ​

恢复窗口并设置窗口大小。

java
driver.manage().window().setSize(new Dimension(1024, 768));

7、得到窗口的位置 ​

获取浏览器窗口左上角的坐标。

java
// 分别获取每个尺寸
int x = driver.manage().window().getPosition().getX();
int y = driver.manage().window().getPosition().getY();

// 或者存储尺寸并在以后查询它们
Point position = driver.manage().window().getPosition();
int x1 = position.getX();
int y1 = position.getY();

8、将窗口移动到设定的位置 ​

java
// 将窗口移动到主显示器的左上角
driver.manage().window().setPosition(new Point(0, 0));

9、最大化窗口 ​

扩大窗口。对于大多数操作系统,窗口将填满屏幕,而不会阻挡操作系统自己的菜单和工具栏。

java
driver.manage().window().maximize();

10、最小化窗口 ​

最小化当前浏览上下文的窗口. 这种命令的精准行为将作用于各个特定的窗口管理器.

最小化窗口通常将窗口隐藏在系统托盘中.

注意: 此功能适用于Selenium 4以及更高版本.

java
driver.manage().window().minimize();

11、全屏窗口 ​

填充整个屏幕,类似于在大多数浏览器中按下 F11。

java
driver.manage().window().fullscreen();

12、屏幕截图 ​

用于捕获当前浏览上下文的屏幕截图. WebDriver端点 屏幕截图 返回以Base64格式编码的屏幕截图.

java
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.*;
import org.openqa.selenium.*;

public class SeleniumTakeScreenshot {
	public static void main(String args[]) throws IOException {
		WebDriver driver = new ChromeDriver();
		driver.get("http://www.example.com");
		File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
		FileUtils.copyFile(scrFile, new File("./image.png"));
		driver.quit();
	}
}

13、元素屏幕截图 ​

用于捕获当前浏览上下文的元素的屏幕截图. WebDriver端点 屏幕截图 返回以Base64格式编码的屏幕截图.

java
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.File;
import java.io.IOException;

public class SeleniumelementTakeScreenshot {
	public static void main(String args[]) throws IOException {
		WebDriver driver = new ChromeDriver();
		driver.get("https://www.example.com");
		WebElement element = driver.findElement(By.cssSelector("h1"));
		File scrFile = element.getScreenshotAs(OutputType.FILE);
		FileUtils.copyFile(scrFile, new File("./image.png"));
		driver.quit();
  }
}

14、打印页面 ​

打印当前浏览器内的页面

注意: 此功能需要无头模式下的Chromium浏览器

java
import org.openqa.selenium.print.PrintOptions;

driver.get("https://www.selenium.dev");
printer = (PrintsPage) driver;

PrintOptions printOptions = new PrintOptions();
printOptions.setPageRanges("1-2");

Pdf pdf = printer.print(printOptions);
String content = pdf.getContent();

7、执行脚本 ​

在当前frame或者窗口的上下文中,执行JavaScript代码片段.

java
//Creating the JavascriptExecutor interface object by Type casting
JavascriptExecutor js = (JavascriptExecutor)driver;
//Button Element
WebElement button =driver.findElement(By.name("btnLogin"));
//Executing JavaScript to click on element
js.executeScript("arguments[0].click();", button);
//Get return value from script
String text = (String) js.executeScript("return arguments[0].innerText", button);
//Executing JavaScript directly
js.executeScript("console.log('hello world')");

8、处理多种Alert弹框 ​

1、只有确定按钮 ​
1、HTML代码 ​

https://www.w3school.com.cn/tiy/t.asp?f=eg_js_alert

html
<!DOCTYPE html>
<html>
<body>

<h1>JavaScript 警告框</h1>

<button onclick="myFunction()">试一试</button>

<script>
function myFunction() {
  alert("我是一个警告框!");
}
</script>

</body>
</html>
2、JAVA代码 ​
java
public static void main(String[] args) {
    System.setProperty("webdriver.chrome.driver", "src/chromedriver.exe");
    WebDriver chromeDriver = new ChromeDriver();
    //只有确定按钮
    chromeDriver.get("https://www.w3school.com.cn/tiy/t.asp?f=eg_js_alert");
    chromeDriver.switchTo().frame("iframeResult");
    chromeDriver.findElement(By.tagName("button")).click();
    Alert alert = chromeDriver.switchTo().alert();
    alert.accept();
}
2、有确定和取消 ​
1、HTML代码 ​

https://www.w3school.com.cn/tiy/t.asp?f=eg_js_confirm

html
<!DOCTYPE html>
<html>
<body>

<h1>JavaScript 确认框</h1>


<button onclick="myFunction()">试一试</button>

<p id="demo"></p>

<script>
function myFunction() {
  var txt;
  if (confirm("Press a button!")) {
    txt = "您按了确定";
  } else {
    txt = "您按了取消";
  }
  document.getElementById("demo").innerHTML = txt;
}
</script>

</body>
</html>
2、JAVA代码 ​
java
public static void main(String[] args) {
    System.setProperty("webdriver.chrome.driver", "src/chromedriver.exe");
    WebDriver chromeDriver = new ChromeDriver();
    //有确定和取消
    chromeDriver.get("https://www.w3school.com.cn/tiy/t.asp?f=eg_js_confirm");
    chromeDriver.switchTo().frame("iframeResult");
    chromeDriver.findElement(By.tagName("button")).click();
    Alert alert = chromeDriver.switchTo().alert();
    alert.accept();
    chromeDriver.findElement(By.tagName("button")).click();
    alert.dismiss();
}
3、有输入框的弹窗 ​
1、HTML代码 ​

https://www.w3school.com.cn/tiy/t.asp?f=eg_js_prompt

html
<!DOCTYPE html>
<html>
<body>

<h1>JavaScript Prompt</h1>

<button onclick="myFunction()">试一试</button>

<p id="demo"></p>

<script>
function myFunction() {
  var txt;
  var person = prompt("请输入您的名字:", "哈利波特");
  if (person == null || person == "") {
    txt = "用户取消输入";
  } else {
    txt = "你好," + person + "!今天过得好吗?";
  }
  document.getElementById("demo").innerHTML = txt;
}
</script>

</body>
</html>
2、JAVA代码 ​
java
public static void main(String[] args) {
    System.setProperty("webdriver.chrome.driver", "src/chromedriver.exe");
    WebDriver chromeDriver = new ChromeDriver();
    //有输入框的弹窗
    chromeDriver.get("https://www.w3school.com.cn/tiy/t.asp?f=eg_js_prompt");
    chromeDriver.switchTo().frame("iframeResult");
    chromeDriver.findElement(By.tagName("button")).click();
    Alert alert = chromeDriver.switchTo().alert();
    alert.sendKeys("沙漠");
    alert.accept();
}
4、有提醒语的弹框 ​
1、HTML代码 ​

https://www.w3school.com.cn/tiy/t.asp?f=eg_js_alert_2

html
<!DOCTYPE html>
<html>
<body>

<h1>JavaScript</h1>

<p>在警告框中换行。</p>

<button onclick="alert('Hello\nHow are you?')">试一试</button>

</body>
</html>
2、JAVA代码 ​
java
public static void main(String[] args) {
    System.setProperty("webdriver.chrome.driver", "src/chromedriver.exe");
    WebDriver chromeDriver = new ChromeDriver();
    //有提醒语的弹框
    chromeDriver.get("https://www.w3school.com.cn/tiy/t.asp?f=eg_js_alert_2");
    chromeDriver.switchTo().frame("iframeResult");
    chromeDriver.findElement(By.tagName("button")).click();
    Alert alert = chromeDriver.switchTo().alert();
    System.err.println(alert.getText());
}

2、元素操作 ​

1、Web元素交互 ​

1、发送键位 ​

元素发送键位命令 将录入提供的键位到 可编辑的 元素. 通常, 这意味着元素是具有 文本 类型的表单的输入元素或具有 内容可编辑 属性的元素. 如果不可编辑, 则返回 无效元素状态 错误.

以下 是WebDriver支持的按键列表.

java
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class HelloSelenium {
	public static void main(String[] args) {
		WebDriver driver = new FirefoxDriver();
			try {
			// Navigate to Url
			driver.get("https://baidu.com");

      // Enter text "q" and perform keyboard action "Enter"
      driver.findElement(By.name("q")).sendKeys("q" + Keys.ENTER);
    } finally {
      driver.quit();
    }
	}
}

2、清除 ​

元素清除命令 重置元素的内容. 这要求元素 可编辑, 且 可重置. 通常, 这意味着元素是具有 文本 类型的表单的输入元素或具有 内容可编辑 属性的元素. 如果不满足这些条件, 将返回 无效元素状态 错误.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class clear {
	public static void main(String[] args) {
		WebDriver driver = new ChromeDriver();
			try {
			// Navigate to Url
			driver.get("https://www.google.com");
			// Store 'SearchInput' element
			WebElement searchInput = driver.findElement(By.name("q"));
			searchInput.sendKeys("selenium");
			// Clears the entered text
			searchInput.clear();
			} finally {
				driver.quit();
			}
		}
}

3、获取元素 ​

通常你会得到一个元素的集合,但是想要使用一个特定的元素,这意味着你需要遍历集合并确定你想要的那个。

java
List<WebElement> elements = driver.findElements(By.tagName("li"));

for (WebElement element : elements) {
    System.out.println("Paragraph text:" + element.getText());
}
1、从元素中查找元素 ​
java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.List;

public class findElementsFromElement {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      driver.get("https://example.com");

      // Get element with tag name 'div'
      WebElement element = driver.findElement(By.tagName("div"));

      // Get all the elements available with tag name 'p'
      List<WebElement> elements = element.findElements(By.tagName("p"));
      for (WebElement e : elements) {
        System.out.println(e.getText());
      }
    } finally {
      driver.quit();
    }
  }
}
2、获取活动元素 ​
java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class activeElementTest {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      driver.get("http://www.google.com");
      driver.findElement(By.cssSelector("[name='q']")).sendKeys("webElement");

      // Get attribute of current active element
      String attr = driver.switchTo().activeElement().getAttribute("title");
      System.out.println(attr);
    } finally {
      driver.quit();
    }
  }
}
3、获取元素CSS值 ​
java
// Navigate to Url
driver.get("https://www.baidu.com");

// Retrieves the computed style property 'color' of linktext
String cssValue = driver.findElement(By.linkText("More information...")).getCssValue("color");
4、获取元素文本 ​
java
// Navigate to url
driver.get("https://example.com");

// Retrieves the text of the element
String text = driver.findElement(By.cssSelector("h1")).getText();

2、定位器 ​

元素选择策略

在 WebDriver 中有 8 种不同的内置元素定位策略:

定位器 Locator描述
class name定位class属性与搜索值匹配的元素(不允许使用复合类名)
css selector定位 CSS 选择器匹配的元素
id定位 id 属性与搜索值匹配的元素
name定位 name 属性与搜索值匹配的元素
link text定位link text可视文本与搜索值完全匹配的锚元素
partial link text定位link text可视文本部分与搜索值部分匹配的锚点元素。如果匹配多个元素,则只选择第一个元素。
tag name定位标签名称与搜索值匹配的元素
xpath定位与 XPath 表达式匹配的元素

1、相对定位器 ​

Selenium 4引入了相对定位器(以前称为友好定位器)。当为所需元素构建定位器并不容易时,这些定位器很有用,但很容易在空间上描述元素相对于具有易于构建定位器的元素的位置。

Selenium 使用 JavaScript 函数 getBoundingClientRect() 来确定页面上元素的大小和位置,并且可以使用此信息来定位相邻元素。 找到相关元素。

相对定位器方法可以作为原点的参数,可以是先前定位的元素参考,也可以是另一个定位器。在这些示例中,我们将仅使用定位器,但您可以将 final 方法中的定位器与元素对象交换,它的工作方式相同。

让我们考虑下面的例子来理解相对定位器。

相对定位器

如果电子邮件文本字段元素由于某种原因不容易识别,但密码文本字段元素是,我们可以使用它是密码元素“上方”的“输入”元素这一事实来定位文本字段元素。

java
By emailLocator = RelativeLocator.with(By.tagName("input")).above(By.id("password"));

如果密码文本字段元素由于某种原因不容易识别,但电子邮件文本字段元素是,我们可以使用它是电子邮件元素“下方”的“输入”元素这一事实来定位文本字段元素。

java
By passwordLocator = RelativeLocator.with(By.tagName("input")).below(By.id("email"));

如果由于某种原因取消按钮不容易识别,但提交按钮元素是,我们可以使用它是提交元素“左侧”的“按钮”元素这一事实来定位取消按钮元素。

java
By cancelLocator = RelativeLocator.with(By.tagName("button")).toLeftOf(By.id("submit"));

如果提交按钮由于某种原因不容易识别,但取消按钮元素是,我们可以使用它是取消元素“右侧”的“按钮”元素这一事实来定位提交按钮元素。

java
By submitLocator = RelativeLocator.with(By.tagName("button")).toRightOf(By.id("cancel"));

如果相对定位不明显,或者根据窗口大小而变化,则可以使用 near 方法来识别最多50px远离提供的定位器的元素。一个很好的用例是使用一个表单元素,该元素没有易于构建的定位器,但其关联的输入标签元素有。

java
By emailLocator = RelativeLocator.with(By.tagName("input")).near(By.id("lbl-email"));

2、链接相对定位器 ​

如果需要,您还可以链接定位器。有时元素最容易被识别为在一个元素的上方/下方和另一个元素的右/左。

java
By submitLocator = RelativeLocator.with(By.tagName("button")).below(By.id("email").toRightOf(By.id("cancel"));

3、选择列表元素 ​

选择元素可能需要大量样板代码才能自动化. 为了减少这种情况并使您的测试更干净, 在Selenium的support包中有一个 Select 类. 要使用它,您将需要以下导入语句:

java
import org.openqa.selenium.support.ui.Select;

然后,您能够参考 <select> 元素,基于WebElement创建一个Select对象。

java
WebElement selectElement = driver.findElement(By.id("selectElementID"));
Select selectObject = new Select(selectElement);

Select对象现在将为您提供一系列命令,使您可以与 <select> 元素进行交互. 首先,有多种方法可以从 <select> 元素中选择一个选项.

html
<select>
 <option value=value1>Bread</option>
 <option value=value2 selected>Milk</option>
 <option value=value3>Cheese</option>
</select>

有三种方法可以从上述元素中选择第一个选项:

java
selectObject.selectByIndex(1);

selectObject.selectByValue("value1");

selectObject.selectByVisibleText("Bread");

然后,您可以检视所有被选择的选项:

java
List<WebElement> allSelectedOptions = selectObject.getAllSelectedOptions();

WebElement firstSelectedOption = selectObject.getFirstSelectedOption();

或者您可能只对 <select> 元素包含哪些 <option> 元素感兴趣:

java
List<WebElement> allAvailableOptions = selectObject.getOptions();

如果要取消选择任何元素,现在有四个选项:

java
selectObject.deselectByIndex(1);

selectObject.deselectByValue("value1");

selectObject.deselectByVisibleText("Bread");

selectObject.deselectAll();

最后,一些 <select> 元素允许您选择多个选项. 您可以通过使用以下命令确定您的 <select> 元素是否允许多选:

java
Boolean doesThisAllowMultipleSelections = selectObject.isMultiple();

3、等待 ​

WebDriver通常可以说有一个阻塞API。因为它是一个指示浏览器做什么的进程外库,而且web平台本质上是异步的,所以WebDriver不跟踪DOM的实时活动状态。这伴随着一些我们将在这里讨论的挑战。

根据经验,大多数由于使用Selenium和WebDriver而产生的间歇性问题都与浏览器和用户指令之间的 竞争条件 有关。例如,用户指示浏览器导航到一个页面,然后在试图查找元素时得到一个 no such element 的错误。

html
<!doctype html>
<meta charset=utf-8>
<title>Race Condition Example</title>

<script>
  var initialised = false;
  window.addEventListener("load", function() {
    var newElement = document.createElement("p");
    newElement.textContent = "Hello from JavaScript!";
    document.body.appendChild(newElement);
    initialised = true;
  });
</script>
java
driver.get("file:///race_condition.html");
WebElement element = driver.findElement(By.tagName("p"));
assertEquals(element.getText(), "Hello from JavaScript!");

这里的问题是WebDriver中使用的默认页面加载策略页面加载策略听从document.readyState在返回调用 navigate 之前将状态改为"complete" 。因为p元素是在文档完成加载之后添加的,所以这个WebDriver脚本可能是间歇性的。它“可能”间歇性是因为无法做出保证说异步触发这些元素或事件不需要显式等待或阻塞这些事件。

幸运的是,WebElement接口上可用的正常指令集——例如 WebElement.click 和 WebElement.sendKeys—是保证同步的,因为直到命令在浏览器中被完成之前函数调用是不会返回的(或者回调是不会在回调形式的语言中触发的)。高级用户交互APIs,键盘和鼠标是例外的,因为它们被明确地设计为“按我说的做”的异步命令。

等待是在继续下一步之前会执行一个自动化任务来消耗一定的时间。

为了克服浏览器和WebDriver脚本之间的竞争问题,大多数Selenium客户都附带了一个 wait 包。在使用等待时,您使用的是通常所说的显式等待

1、显式等待 ​

显示等待 是Selenium客户可以使用的命令式过程语言。它们允许您的代码暂停程序执行,或冻结线程,直到满足通过的 条件 。这个条件会以一定的频率一直被调用,直到等待超时。这意味着只要条件返回一个假值,它就会一直尝试和等待

由于显式等待允许您等待条件的发生,所以它们非常适合在浏览器及其DOM和WebDriver脚本之间同步状态。

为了弥补我们之前的错误指令集,我们可以使用等待来让 findElement 调用等待直到脚本中动态添加的元素被添加到DOM中:

java
WebDriver driver = new ChromeDriver();
driver.get("https://baidu.com/ncr");
driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER);
// Initialize and wait till element(link) became clickable - timeout in 10 seconds
WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10))
        .until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")));
// Print the first result
System.out.println(firstResult.getText());

我们将 条件 作为函数引用传递, 等待 将会重复运行直到其返回值为true。“truthful”返回值是在当前语言中计算为boolean true的任何值,例如字符串、数字、boolean、对象(包括 WebElement )或填充(非空)的序列或列表。这意味着 空列表 的计算结果为false。当条件为true且阻塞等待终止时,条件的返回值将成为等待的返回值。

有了这些知识,并且因为等待实用程序默认情况下会忽略 no such element 的错误,所以我们可以重构我们的指令使其更简洁:

java
WebElement foo = new WebDriverWait(driver, Duration.ofSeconds(3))
          .until(driver -> driver.findElement(By.name("q")));
assertEquals(foo.getText(), "Hello from JavaScript!");

在这个示例中,我们传递了一个匿名函数(但是我们也可以像前面那样显式地定义它,以便重用它)。传递给我们条件的第一个,也是唯一的一个参数始终是对驱动程序对象 WebDriver 的引用。在多线程环境中,您应该小心操作传入条件的驱动程序引用,而不是外部范围中对驱动程序的引用。

因为等待将会吞没在没有找到元素时引发的 no such element 的错误,这个条件会一直重试直到找到元素为止。然后它将获取一个 WebElement 的返回值,并将其传递回我们的脚本。

如果条件失败,例如从未得到条件为真实的返回值,等待将会抛出/引发一个叫 timeout error 的错误/异常。

选项

等待条件可以根据您的需要进行定制。有时候是没有必要等待缺省超时的全部范围,因为没有达到成功条件的代价可能很高。

等待允许你传入一个参数来覆盖超时:

java
new WebDriverWait(driver, Duration.ofSeconds(3)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")));

2、隐式等待 ​

还有第二种区别于显示等待类型的 隐式等待 。通过隐式等待,WebDriver在试图查找_任何_元素时在一定时间内轮询DOM。当网页上的某些元素不是立即可用并且需要一些时间来加载时是很有用的。

默认情况下隐式等待元素出现是禁用的,它需要在单个会话的基础上手动启用。将显式等待和隐式等待混合在一起会导致意想不到的结果,就是说即使元素可用或条件为真也要等待睡眠的最长时间。

警告: 不要混合使用隐式和显式等待。这样做会导致不可预测的等待时间。例如,将隐式等待设置为10秒,将显式等待设置为15秒,可能会导致在20秒后发生超时。

隐式等待是告诉WebDriver如果在查找一个或多个不是立即可用的元素时轮询DOM一段时间。默认设置为0,表示禁用。一旦设置好,隐式等待就被设置为会话的生命周期。

java
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));

3、流畅等待 ​

流畅等待实例定义了等待条件的最大时间量,以及检查条件的频率。

用户可以配置等待来忽略等待时出现的特定类型的异常,例如在页面上搜索元素时出现的NoSuchElementException。

java
// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  .withTimeout(Duration.ofSeconds(30))
  .pollingEvery(Duration.ofSeconds(5))
  .ignoring(NoSuchElementException.class);

WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
  public WebElement apply(WebDriver driver) {
    return driver.findElement(By.id("foo"));
  }
});

4、Actions ​

1、键盘 ​

Keyboard代表一个键盘事件. Keyboard操作通过使用底层接口允许我们向web浏览器提供虚拟设备输入.

除了由常规 unicode 表示的键之外,还为其他键盘键分配了 unicode 值以与 Selenium 一起使用。每种语言都有自己的方式来引用这些键;完整列表可以在 这里找到。

1、Key down ​

keyDown用于模拟按下辅助按键(CONTROL, SHIFT, ALT)的动作.

java
WebDriver driver = new ChromeDriver();
try {
  // Navigate to Url
  driver.get("https://google.com");

  // Enter "webdriver" text and perform "ENTER" keyboard action
  driver.findElement(By.name("q")).sendKeys("webdriver" + Keys.ENTER);

  Actions actionProvider = new Actions(driver);
  Action keydown = actionProvider.keyDown(Keys.CONTROL).sendKeys("a").build();
  keydown.perform();
} finally {
  driver.quit();
}

2、Key up ​

keyUp用于模拟辅助按键(CONTROL, SHIFT, ALT)弹起或释放的操作.

java
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;

public class HelloSelenium {
  public static void main(String[] args) {
    WebDriver driver = new FirefoxDriver();
    try {
      // Navigate to Url
      driver.get("https://google.com");
      Actions action = new Actions(driver);

      // Store google search box WebElement
      WebElement search = driver.findElement(By.name("q"));

      // Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty)
      action.keyDown(Keys.SHIFT).sendKeys(search,"qwerty").keyUp(Keys.SHIFT).sendKeys("qwerty").perform();
    } finally {
      driver.quit();
    }
  }
}

2、鼠标 ​

1、clickAndHold ​

它将移动到该元素,然后在给定元素的中间单击(不释放).

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class clickAndHold {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://baidu.com");

      // Store 'google search' button web element
      WebElement searchBtn = driver.findElement(By.linkText("Sign in"));
      Actions actionProvider = new Actions(driver);
      // Perform click-and-hold action on the element
      actionProvider.clickAndHold(searchBtn).build().perform();
    } finally {
      driver.quit();
    }
  }
}

2、contextClick ​

此方法首先将鼠标移动到元素的位置, 然后在给定元素执行上下文点击(右键单击).

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class contextClick {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://google.com");

      // Store 'google search' button web element
      WebElement searchBtn = driver.findElement(By.linkText("Sign in"));
      Actions actionProvider = new Actions(driver);
      // Perform context-click action on the element
      actionProvider.contextClick(searchBtn).build().perform();
    } finally {
      driver.quit();
    }
  }
}

3、doubleClick ​

它将移动到该元素, 并在给定元素的中间双击.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class doubleClick {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://google.com");

      // Store 'google search' button web element
      WebElement searchBtn = driver.findElement(By.linkText("Sign in"));
      Actions actionProvider = new Actions(driver);
      // Perform double-click action on the element
      actionProvider.doubleClick(searchBtn).build().perform();
    } finally {
      driver.quit();
    }
  }
}

4、moveToElement ​

此方法将鼠标移到元素的中间. 执行此操作时, 该元素也会滚动到视图中.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class moveToElement {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://google.com");

      // Store 'Gmail' anchor web element
      WebElement gmailLink = driver.findElement(By.linkText("Gmail"));
      Actions actionProvider = new Actions(driver);
      // Performs mouse move action onto the element
      actionProvider.moveToElement(gmailLink).build().perform();
    } finally {
      driver.quit();
    }
  }
}

5、moveByOffset ​

此方法将鼠标从其当前位置(或0,0)移动给定的偏移量. 如果坐标在视图窗口之外, 则鼠标最终将在浏览器窗口之外.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class moveByOffset {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://google.com");

      // Store 'Gmail' anchor web element
      WebElement gmailLink = driver.findElement(By.linkText("Gmail"));
      // Capture x and y offset positions of element
      int xOffset = gmailLink.getRect().getX();
      int yOffset = gmailLink.getRect().getY();
      Actions actionProvider = new Actions(driver);
      // Performs mouse move action onto the offset position
      actionProvider.moveByOffset(xOffset, yOffset).build().perform();
    } finally {
      driver.quit();
    }
  }
}

6、dragAndDrop ​

此方法首先在源元素上单击并按住,然后移动到目标元素的位置后释放鼠标.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class dragAndDrop {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://crossbrowsertesting.github.io/drag-and-drop");
      // Store 'box A' as source element
      WebElement sourceEle = driver.findElement(By.id("draggable"));
      // Store 'box B' as source element
      WebElement targetEle = driver.findElement(By.id("droppable"));
      Actions actionProvider = new Actions(driver);
      // Performs drag and drop action of sourceEle onto the targetEle
      actionProvider.dragAndDrop(sourceEle, targetEle).build().perform();
    } finally {
      driver.quit();
    }
  }
}

7、dragAndDropBy ​

此方法首先在源元素上单击并按住, 移至给定的偏移量后释放鼠标.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class dragAndDropBy {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://crossbrowsertesting.github.io/drag-and-drop");
      // Store 'box A' as source element
      WebElement sourceEle = driver.findElement(By.id("draggable"));
      // Store 'box B' as source element
      WebElement targetEle = driver.findElement(By.id("droppable"));
      int targetEleXOffset = targetEle.getLocation().getX();
      int targetEleYOffset = targetEle.getLocation().getY();
      Actions actionProvider = new Actions(driver);
      // Performs dragAndDropBy onto the  target element offset position
      actionProvider.dragAndDropBy(sourceEle, targetEleXOffset, targetEleYOffset).build().perform();
    } finally {
      driver.quit();
    }
  }
}

8、release ​

此操作将释放按下的鼠标左键. 如果WebElement转移了, 它将释放给定WebElement上按下的鼠标左键.

java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class release {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      // Navigate to Url
      driver.get("https://crossbrowsertesting.github.io/drag-and-drop");
      // Store 'box A' as source element
      WebElement sourceEle = driver.findElement(By.id("draggable"));
      // Store 'box B' as source element
      WebElement targetEle = driver.findElement(By.id("droppable"));
      Actions actionProvider = new Actions(driver);
      actionProvider.clickAndHold(sourceEle).moveToElement(targetEle).build().perform();
      // Performs release event
      actionProvider.release().build().perform();
    } finally {
      driver.quit();
    }
  }
}

5、双向协议 ​

1、Chrome开发工具协议 ​

许多浏览器都提供"开发工具" – 一组与浏览器集成的工具, 开发人员可以用其调试web应用程序并探索其页面的性能. 谷歌浏览器开发工具 使用一种称为Chrome DevTools Protocol (简称"CDP") 的协议. 顾名思义, 这不是为测试而设计的, 而并没有一个稳定的API, 所以它的功能高度依赖于浏览器的版本.

WebDriver Bidi是W3C WebDriver的下一代协议, 旨在提供由所有浏览器实现稳定的API, 但尚未完成. 在此之前, Selenium提供了通过CDP实现的方式 (诸如Google Chrome或Microsoft Edge, 以及Firefox), 允许您以有趣的方式增强测试. 下面给出了实际使用的例子.

1、模拟地理位置 ​

一些应用程序在不同的位置具有不同的特性和功能. 自动化此类应用程序很难, 因为很难使用Selenium在浏览器中模拟地理位置. 但是在Devtools的帮助下, 我们可以轻易模拟他们. 下面的代码片段演示了这一点.

java
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),
                                               Optional.of(13.4501),
                                               Optional.of(1)));
driver.get("https://my-location.org/");
driver.quit();

2、通过远程WebDriver模拟地理位置 ​

java
ChromeOptions chromeOptions = new ChromeOptions();
WebDriver driver = new RemoteWebDriver(new URL("<grid-url>"), chromeOptions);
driver = new Augmenter().augment(driver);

DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();

devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),
                                               Optional.of(13.4501),
                                               Optional.of(1)));
 
driver.get("https://my-location.org/");
driver.quit();

3、覆盖设备模式 ​

使用Selenium与CDP的集成, 可以覆盖当前设备模式并模拟新模式. Width, height, mobile和deviceScaleFactor是必需的参数. 可选参数包括scale, screenWidth, screenHeight, positionX, positionY, dontSetVisible, screenOrientation, viewport和displayFeature.

java
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
// iPhone 11 Pro dimensions
devTools.send(Emulation.setDeviceMetricsOverride(375,
                                                 812,
                                                 50,
                                                 true,
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty(),
                                                 Optional.empty()));
driver.get("https://selenium.dev/");
driver.quit();

4、收集性能指标 ​

在浏览应用程序时收集各种性能指标。

java
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;

public void performanceMetricsExample() {
    ChromeDriver driver = new ChromeDriver();
    DevTools devTools = driver.getDevTools();
    devTools.createSession();
    devTools.send(Performance.enable(Optional.empty()));
    List<Metric> metricList = devTools.send(Performance.getMetrics());

    driver.get("https://google.com");
    driver.quit();

    for(Metric m : metricList) {
        System.out.println(m.getName() + " = " + m.getValue());
    }
}

2、双向接口 ​

1、注册基本身份验证 ​

一些应用程序利用浏览器身份验证来保护页面。使用 Selenium,您可以在出现基本身份验证凭据时自动输入它们。

java
Predicate<URI> uriPredicate = uri -> uri.getHost().contains("your-domain.com");

((HasAuthentication) driver).register(uriPredicate, UsernameAndPassword.of("admin", "password"));
driver.get("https://your-domain.com/login");

2、突变观察 ​

Mutation Observation 是当 DOM 中的特定元素上存在 DOM 突变时,通过 WebDriver BiDi 捕获事件的能力。

java
ChromeDriver driver = new ChromeDriver();

AtomicReference<DomMutationEvent> seen = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
((HasLogEvents) driver).onLogEvent(domMutation(mutation -> {
    seen.set(mutation);
    latch.countDown();
}));

driver.get("https://www.google.com");
WebElement span = driver.findElement(By.cssSelector("span"));

((JavascriptExecutor) driver).executeScript("arguments[0].setAttribute('cheese', 'gouda');", span);

assertThat(latch.await(10, SECONDS), is(true));
assertThat(seen.get().getAttributeName(), is("cheese"));
assertThat(seen.get().getCurrentValue(), is("gouda"));

driver.quit();

3、收听console.log事件 ​

监听console.log事件并注册回调以处理事件。

java
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
devTools.send(Log.enable());
devTools.addListener(Log.entryAdded(),
                           logEntry -> {
                               System.out.println("log: "+logEntry.getText());
                               System.out.println("level: "+logEntry.getLevel());
                           });
driver.get("http://the-internet.herokuapp.com/broken_images");
// Check the terminal output for the browser console messages.
driver.quit();

4、监听 JS 异常 ​

侦听 JS 异常并注册回调以处理异常详细信息。

java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;

public void jsExceptionsExample() {
    ChromeDriver driver = new ChromeDriver();
    DevTools devTools = driver.getDevTools();
    devTools.createSession();

    List<JavascriptException> jsExceptionsList = new ArrayList<>();
    Consumer<JavascriptException> addEntry = jsExceptionsList::add;
    devTools.getDomains().events().addJavascriptExceptionListener(addEntry);

    driver.get("<your site url>");

    WebElement link2click = driver.findElement(By.linkText("<your link text>"));
    ((JavascriptExecutor) driver).executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);",
          link2click, "onclick", "throw new Error('Hello, world!')");
    link2click.click();

    for (JavascriptException jsException : jsExceptionsList) {
        System.out.println("JS exception message: " + jsException.getMessage());
        System.out.println("JS exception system information: " + jsException.getSystemInformation());
        jsException.printStackTrace();
    }
}

5、网络拦截 ​

如果您想捕获进入浏览器的网络事件并对其进行操作,您可以使用以下示例来实现。

java
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.NetworkInterceptor;
import org.openqa.selenium.remote.http.Contents;
import org.openqa.selenium.remote.http.Filter;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Route;

NetworkInterceptor interceptor = new NetworkInterceptor(
  driver,
  Route.matching(req -> true)
  .to(() -> req -> new HttpResponse()
      .setStatus(200)
      .addHeader("Content-Type", MediaType.HTML_UTF_8.toString())
      .setContent(utf8String("Creamy, delicious cheese!"))));

driver.get("https://example-sausages-site.com");

String source = driver.getPageSource();

assertThat(source).contains("delicious cheese!");

6、远程WebDriver(先了解) ​

您可以如本地一样, 使用远程WebDriver. 主要区别在于需要配置远程WebDriver, 以便可以在不同的计算机上运行测试.

远程WebDriver由两部分组成:客户端和服务端. 客户端是您的WebDriver测试,而服务端仅仅是可以被托管于任何现代Java EE应用服务器的Java Servlet.

要运行远程WebDriver客户端, 我们首先需要连接到RemoteWebDriver. 为此, 我们将URL指向运行测试的服务器的地址. 为了自定义我们的配置, 我们设置了既定的功能. 下面是一个实例化样例, 其指向我们的远程Web服务器 www.example.com 的远程WebDriver对象, 并在Firefox上运行测试.

java
FirefoxOptions firefoxOptions = new FirefoxOptions();
WebDriver driver = new RemoteWebDriver(new URL("http://www.example.com"), firefoxOptions);
driver.get("http://www.google.com");
driver.quit();

为了进一步自定义测试配置, 我们可以添加其他既定的功能.

1、浏览器选项 ​

例如, 假设您想使用Chrome版本67 在Windows XP上运行Chrome:

java
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setCapability("browserVersion", "67");
chromeOptions.setCapability("platformName", "Windows XP");
WebDriver driver = new RemoteWebDriver(new URL("http://www.example.com"), chromeOptions);
driver.get("http://www.baidu.com");
driver.quit();

2、本地文件检测器 ​

本地文件检测器允许将文件从客户端计算机传输到远程服务器. 例如, 如果测试需要将文件上传到Web应用程序, 则远程WebDriver可以在运行时 将文件从本地计算机自动传输到远程Web服务器. 这允许从运行测试的远程计算机上载文件. 默认情况下未启用它, 可以通过以下方式启用:

java
driver.setFileDetector(new LocalFileDetector());

定义上述代码后, 您可以通过以下方式在测试中上传文件:

java
driver.get("http://sso.dev.saucelabs.com/test/guinea-file-upload");
WebElement upload = driver.findElement(By.id("myfile"));
upload.sendKeys("/Users/sso/the/local/path/to/darkbulb.jpg");

3、追踪客户端请求 ​

此功能仅适用于Java客户端绑定 (Beta版以后). 远程WebDriver客户端向Selenium网格服务器发送请求, 后者将请求传递给WebDriver. 应该在服务器端和客户端启用跟踪, 以便端到端地追踪HTTP请求. 两端都应该有一个指向可视化框架的追踪导出器设置. 默认情况下, 对客户端和服务器都启用追踪. 若设置可视化框架Jaeger UI及Selenium Grid 4, 请参阅所需版本的追踪设置 .

对于客户端设置, 请执行以下步骤.

1、添加所需依赖 ​

xml
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-jaeger</artifactId>
  <version>0.14.0</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty</artifactId>
  <version>1.34.1</version>
</dependency>

2、在运行客户端时添加/传递所需的系统属性 ​

java
System.setProperty("JAEGER_SERVICE_NAME", "selenium-java-client");
System.setProperty("JAEGER_AGENT_HOST","localhost");
System.setProperty("JAEGER_AGENT_PORT","14250");

ImmutableCapabilities capabilities = new ImmutableCapabilities("browserName", "chrome");

WebDriver driver = new RemoteWebDriver(new URL("http://www.example.com"), capabilities);

driver.get("http://www.google.com");

driver.quit();

可以使用Maven安装追踪导出器的外部库. 在项目pom.xml中添加 opentelemetry-exporter-jaeger 和 grpc-netty 的依赖项:

xml
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-jaeger</artifactId>
  <version>1.0.0</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty</artifactId>
  <version>1.35.0</version>
</dependency>

3、在运行客户端时添加/传递所需的系统属性 ​

java
System.setProperty("otel.traces.exporter", "jaeger");
System.setProperty("otel.exporter.jaeger.endpoint", "http://localhost:14250");
System.setProperty("otel.resource.attributes", "service.name=selenium-java-client");

ImmutableCapabilities capabilities = new ImmutableCapabilities("browserName", "chrome");

WebDriver driver = new RemoteWebDriver(new URL("http://www.example.com"), capabilities);

driver.get("http://www.google.com");

driver.quit();

7、额外功能 ​

1、颜色 ​

在测试中, 您偶尔会需要验证某事物的颜色;问题是网络上的颜色定义不是个常量. 如果有一种简单的方法可以比较颜色的十六进制与RGB呈现, 或者颜色的RGBA与HSLA呈现, 岂不美哉?

首先, 您需要导入该类:

java
import org.openqa.selenium.support.Color;

您现在可以开始创建颜色对象. 每个颜色对象都需要使用您颜色的字符串定义来创建. 支持的颜色定义如下:

java
private final Color HEX_COLOUR = Color.fromString("#2F7ED8");
private final Color RGB_COLOUR = Color.fromString("rgb(255, 255, 255)");
private final Color RGB_COLOUR = Color.fromString("rgb(40%, 20%, 40%)");
private final Color RGBA_COLOUR = Color.fromString("rgba(255, 255, 255, 0.5)");
private final Color RGBA_COLOUR = Color.fromString("rgba(40%, 20%, 40%, 0.5)");
private final Color HSL_COLOUR = Color.fromString("hsl(100, 0%, 50%)");
private final Color HSLA_COLOUR = Color.fromString("hsla(100, 0%, 50%, 0.5)");

Color类还支持在以下网址中指定的所有基本颜色定义 http://www.w3.org/TR/css3-color/#html4.

java
private final Color BLACK = Color.fromString("black");
private final Color CHOCOLATE = Color.fromString("chocolate");
private final Color HOTPINK = Color.fromString("hotpink");

如果元素上未设置颜色, 则有时浏览器会返回“透明”的颜色值. Color类也支持此功能:

java
private final Color TRANSPARENT = Color.fromString("transparent");

现在, 您可以安全地查询元素以获取其颜色/背景色, 任何响应都将被正确解析并转换为有效的Color对象:

java
Color loginButtonColour = Color.fromString(driver.findElement(By.id("login")).getCssValue("color"));

Color loginButtonBackgroundColour = Color.fromString(driver.findElement(By.id("login")).getCssValue("background-color"));

然后, 您可以直接比较颜色对象:

java
assert loginButtonBackgroundColour.equals(HOTPINK);

或者, 您可以将颜色转换为以下格式之一并执行静态验证:

java
assert loginButtonBackgroundColour.asHex().equals("#ff69b4");
assert loginButtonBackgroundColour.asRgba().equals("rgba(255, 105, 180, 1)");
assert loginButtonBackgroundColour.asRgb().equals("rgb(255, 105, 180)");

2、线程守卫 ​

此类仅在Java中可用

ThreadGuard检查是否仅从创建驱动程序的同一线程中调用了驱动程序. 线程问题 (尤其是在Parallel中运行测试时) 可能遇到神秘并且难以诊断错误. 使用此包装器可以防止此类错误, 并且在发生此类情况时会抛出异常.

以下的示例模拟一种线程冲突的情况:

java
public class DriverClash {
  //thread main (id 1) created this driver
  private WebDriver protectedDriver = ThreadGuard.protect(new ChromeDriver());

  static {
    System.setProperty("webdriver.chrome.driver", "<Set path to your Chromedriver>");
  }

  //Thread-1 (id 24) is calling the same driver causing the clash to happen
  Runnable r1 = () -> {protectedDriver.get("https://selenium.dev");};
  Thread thr1 = new Thread(r1);

  void runThreads(){
    thr1.start();
  }

  public static void main(String[] args) {
    new DriverClash().runThreads();
  }
}

结果如下所示:

java
Exception in thread "Thread-1" org.openqa.selenium.WebDriverException:
Thread safety error; this instance of WebDriver was constructed
on thread main (id 1)and is being accessed by thread Thread-1 (id 24)
This is not permitted and *will* cause undefined behaviour

正如示例所示:

  • protectedDriver 将在主线程中创建
  • 我们使用Java的 Runnable 启动一个新进程, 并使用一个新的 Thread 运行该进程
  • 这两个 Thread 都会发生冲突, 因为主线程的内存中没有 protectedDriver
  • ThreadGuard.protect 会抛出异常