Selenium
/səˈliːniəm/
1. 简介
Selenium是一个用于Web应用程序自动化测试工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE、Mozilla Firefox、Safari、Google Chrome、Opera、Edge等。
适用于自动化测试,js动态爬虫(破解反爬虫)等领域。
Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此可以用于任何支持JavaScript的浏览器上。selenium可以模拟真实浏览器,自动化测试工具,支持多种浏览器。
Selenium官方
2. 组成
- Selenium IDE:嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作录制与回放功能,主要用于快速创建BUG及重现脚本,可转化为多种语言;
- Selenium RC: 核心组件,支持多种不同语言编写自动化测试脚本,通过其服务器作为代理服务器去访问应用,达到测试的目的;
- Selenium WebDriver(重点):一个浏览器自动化框架,它接受命令并将它们发送到浏览器。它是通过特定于浏览器的驱动程序实现的。它直接与浏览器通信并对其进行控制。Selenium WebDriver支持各种编程语言,如Java、C# 、PHP、Python、Perl、Ruby;
- Selenium grid:测试辅助工具,用于做分布式测试,可以并行执行多个测试任务,提升测试效率。
3. 特点
- 开源、免费
- 多浏览器支持:FireFox、Chrome、IE、Opera、Edge;
- 多平台支持:Linux、Windows、MAC;
- 多语言支持:Java、Python、Ruby、C#、JavaScript、C++;
- 对Web页面有良好的支持;
- 简单(API 简单)、灵活(用开发语言驱动);
- 支持分布式测试用例执行。
4. 配置
采用Java编写自动化测试代码。
4.1 环境依赖
注意:浏览器版本必须与Selenium驱动版本一致!
Edge:118.0.2088.76 x64
Selenium驱动:118.0.2088.76
Maven依赖:4.8.0
JDK:1.8.0_361
4.2 下载对应浏览器的驱动
注意:下载的驱动包需要与当前浏览器版本对对应。
4.4 配置驱动包
将驱动包解压到jdk / bin目录下。
5. 运行
5.1 创建Maven项目
5.2 导入Selenium依赖
pom.xml
<dependencies><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.8.0</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>
</dependencies>
5.3 在test /java下创建类
或者直接在
src -> main -> java
下新建。
5.4 在自动化测试类中编写代码
// 自动化测试类
public class AutoTest {public void Test() throws InterruptedException, IOException {// Edge驱动EdgeOptions edgeOptions = new EdgeOptions();// 允许所有请求(允许浏览器通过远程服务器访问不同源的网页,即跨域访问)edgeOptions.addArguments("--remote-allow-origins=*");EdgeDriver edgeDriver = new EdgeDriver(edgeOptions);// 启动需要打开的网页edgeDriver.get("https://www.baidu.com");// 退出edgeDriver.quit();}
}
import java.io.IOException;// 启动类
public class RunAutoTest {public static void main(String[] args) throws InterruptedException, IOException {AutoTest autoTest = new AutoTest();autoTest.Test();}
}
5.5 启动
6. 使用
6.1 请求
方法 | 说明 |
---|---|
driver.get(url) | 请求一个页面,不支持前进和后退切换 |
driver.navigate().to(url) | 和get类似,支持前进和后退切换 |
driver.navigate().forward(url) | 指前进到下一个页面(必须后退一个页面后才能前进) |
driver.navigate().back(url) | 退到上一个页面(必须前进了一个页面才能回退) |
driver.navigate().refresh(url) | 刷新当前页面 |
6.2 定位元素
方法 | 描述 |
---|---|
driver.findElement(By.id()) | 使用元素的唯一标识ID进行定位 |
driver.findElement(By.name()) | 使用元素的名称属性进行定位 |
driver.findElement(By.className()) | 使用元素的类名进行定位 |
driver.findElement(By.tagName) | 使用元素的标签名进行定位 |
driver.findElement(By.partialLinkText()) | 使用链接文本进行定位 |
driver.findElement(By.cssSelector) | 使用CSS选择器进行定位 |
driver.findElement(By.xpath) | 使用XPath表达式进行定位 |
cssSelector:
cv工程
元素定位—cssSelector
根据tagName
By.cssSelector("input");// 不推荐,tagName会重复
根据ID
By.cssSelector("input#id"); By.cssSelector("#id");
根据className(样式名)
By.cssSelector(".className"); By.cssSelector("input.className");
css精确定位
根据元素属性,属性名=属性值(id、calss等都可使用此形式)
// By.cssSelector("标签名[属性名='属性值']"); By.cssSelector("input[name='xxx']");
多属性
By.cssSelector("标签名[属性名='属性值'][属性名='属性值']");
6.3 获取内容
方法 | 说明 |
---|---|
String getPageSource() | 获取页面html |
String getTitle() | 获取页面标题 |
String getText() | 获取此元素(包括子元素)的可见(即未被CSS隐藏)文本 |
String getTagName() | 获取此元素的标签名 |
String getAttribute(String name) | 获取元素指定属性的值 |
Point getLocation() | 获取当前元素,基于页面左上角的为准 |
Dimension getSize() | 渲染元素的宽度和高度是多少 |
String getCurrentUrl() | 获取表示浏览器正在查看的当前URL的字符串 |
String getCssValue(String propertyName) | 获取指定元素的CSS属性的值 |
6.4 操作行为
方法 | 说明 |
---|---|
clear() | 如果该元素是文本输入元素,则会清除该值 |
submit() | 提交from表单 |
click() | 单击此元素 |
sendKeys() | 使用此方法模拟在元素中键入,可以设置其值 |
注意:
有些页面是使用动态加载js的,会导致html页面正常显示,但js还没执行完毕,相关事件还没绑定到具体的元素上。导致出现操作无效或程序抛出异常。
可以在加载页面时强制等待,全部加载完毕后再进行后续操作。
6.5 窗口
方法 | 说明 |
---|---|
driver.manage().window().setSize(new Dimension(1024, 768)) | 设置当前窗口的大小 |
driver.manage().window().setPosition() | 设置当前窗口的位置(相对于屏幕的左上角) |
driver.manage().window().getSize() | 获取当前窗口的大小 |
driver.manage().window().getPosition() | 获取当前窗口相对于屏幕左上角的位置 |
driver.manage().window().fullScreen() | 全屏当前窗口 |
driver.manage().window().maximize() | 最大化当前窗口 |
6.6 切换指定窗口
当浏览器每次打开一个标签页的时候,会自动的给一个标签进行标识,这个标识我们称之为“句柄”。
方法 | 说明 |
---|---|
String getWindowHandle() | 返回当前窗口句柄,通过将其传递给switchTo(),进行切换窗口 |
driver.switchTo().window(windowHandle) | 切换到指定句柄的窗口 |
String curHandle = edgeDriver.getWindowHandle();// 获取当前页面句柄System.out.println("当前句柄:" + curHandle);Set<String> handles = edgeDriver.getWindowHandles();// 获取所有页面句柄for (String handle : handles) {if (!handle.equals(curHandle)) {// 发生页面切换edgeDriver.switchTo().window(handle);// 切换窗口}}
6.7 iframe切换
方法 | 说明 |
---|---|
driver.switchTo().frame(index) | 根据index进行切换 |
driver.switchTo().frame(id) | 根据id进行切换 |
driver.switchTo().frame(name) | 根据name进行切换 |
driver.switchTo().frame(WebElement) | 根据WebElement进行切换 |
driver.switchTo().defaultContent() | 切换后,回到默认内容页面(否则会找不到元素) |
6.8 模拟键盘和鼠标
6.8.1 键盘事件
方法 | 说明 |
---|---|
driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “a”) | ctrl+a |
driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “c”) | ctrl+c |
driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “v”) | ctrl+v |
driver.findElement(By.id(“kw”)).sendKeys(Keys.TAB) | TAB键 |
driver.findElement(By.id(“kw”)).sendKeys(Keys.ENTER) | 回车键 |
driver.findElement(By.id(“kw”)).sendKeys(Keys.SPACE) | 空格键 |
6.8.2 鼠标事件
Actions actions = new Actions(driver);
方法 | 说明 |
---|---|
actions.contextClick(element).perform() | 右键点击enement的元素 |
actions.clickAndHold(element).perform() | 左键单击enement元素 |
actions.doubleClick(element).perform() | 鼠标左键双击enement元素 |
actions.moveToElement(element).perform() | 鼠标悬停enement元素(中间) |
actions.moveToElement(element,x,y).perform() | 鼠标悬停enement元素(指定位置) |
actions.moveToElement(x,y).perform(); | 将鼠标从其当前位置移动鼠标 |
Actions dragAndDrop(WebElement source, WebElement target) | 拖动元素, 在源元素的位置执行点击并保持,移动到目标元素的位置,然后释放鼠标 |
Actions dragAndDropBy(WebElement source, int xOffset, int yOffset) 拖动到指定位置 | 拖动到指定位置 |
6.9 等待
6.9.1 强制等待
Thread.sleep(millisecond);
程序阻塞执行,不建议。
6.9.2 显式等待
显式等待是添加到代码中的循环,在应用程序退出循环并继续执行代码中的下一个命令之前,它会轮询应用程序以查找特定条件以评估为true。如果在指定的超时值之前未满足条件,则代码将给出超时错误。
WebElement revealed = driver.findElement(By.id("revealed"));
Wait<WebDriver> wait = new WebDriverWait(driver, Duration.ofSeconds(2));driver.findElement(By.id("reveal")).click();
wait.until(d -> revealed.isDisplayed());revealed.sendKeys("Displayed");
6.9.3 隐式等待
这是一个全局设置,适用于整个会话的每个元素位置调用。默认值为0,这意味着如果找不到元素,它将立即返回错误。如果设置了隐式等待,则在返回错误之前,驱动程序将等待所提供值的持续时间。请注意,一旦找到元素,驱动程序将返回元素引用,代码将继续执行,因此较大的隐式等待值不一定会增加会话的持续时间。
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
注意:
不要混合隐式和显式等待。这样做可能会导致不可预测的等待时间。
例如,设置10秒的隐式等待和15秒的显式等待可能会导致20秒后发生超时。
6.10 弹窗
- 警告弹窗中, 切换到弹窗,调用
accept()
- 确认弹窗中, 切换到弹窗,调用
accept()
或者dismiss()
- 提示弹窗中, 切换到弹窗, 可以先输入文本, 调用
sendKeys()
, 然后调用accept()
或者dismiss()
.
提示:这里的sendKeys()
在页面上看不到输入文本的执行效果
driver.get("...");
Thread.sleep(3000);// 打开弹窗
driver.findElement(By.cssSelector("body > input[type=button]")).click();
Thread.sleep(3000);// 切换到弹窗进行弹窗的处理
Alert alert = driver.switchTo().alert();
Thread.sleep(3000);// 弹窗输入文本
alert.sendKeys("abcdef");// 点击确认
alert.accept();// 点击取消
// alert.dismiss();driver.quit();
6.10.1 警告弹窗
警告弹窗中, 切换到弹窗,调用 accept()
6.10.2 确认弹窗
确认弹窗中, 切换到弹窗,调用 accept()
或者 dismiss()
6.10.3 提示弹窗
提示弹窗中, 切换到弹窗, 可以先输入文本, 调用 sendKeys()
, 然后调用 accept()
或者 dismiss()
.
提示:这里的 sendKeys()
在页面上看不到输入文本的执行效果
6.11 下拉选择框
方法 | 说明 |
---|---|
select.getOptions() | 获取所有选项 |
select.selectByIndex(index) | 根据索引选中对应的元素 |
select.selectByValue(value) | 选择指定value值对应的选项 |
select.selectByVisibleText(text) | 选中文本值对应的选项 |
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body>
<select name="ss" id="sss"><option value="12.5">**12.5**</option><option value="6.6">**6.6**</option><option value="486.4">**486.4**</option><option value="21.12">**21.12**</option><option value="99.99">**99.99**</option>
</select>
</body>
</html>
package org.example;import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.support.ui.Select;import java.time.Duration;public class Main {public static void main(String[] args) throws Exception {EdgeOptions edgeOptions = new EdgeOptions();edgeOptions.addArguments("--remote-allow-origins=*");EdgeDriver edgeDriver = new EdgeDriver(edgeOptions);edgeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));// 隐式等待edgeDriver.get("file:///D:/idea-workspace/WebTest/src/main/resources/web/fileUpload/select.html");// WebElement webElement = edgeDriver.findElement(By.cssSelector("sss"));
// Select select = new Select(webElement);Select select = new Select(edgeDriver.findElement(By.cssSelector("#sss")));// 根据文本选择Thread.sleep(3000);select.selectByVisibleText("**99.99**");// 根据索引值选择Thread.sleep(3000);select.selectByIndex(1);// 根据属性值选择Thread.sleep(3000);select.selectByValue("6.6");Thread.sleep(1000);edgeDriver.quit();}
}
6.11.1 根据文本选择
WebElement webElement = driver.findElement(By.cssSelector("#Method"));// 创建选择框对象(将此web元素封装为Select对象)
Select select = new Select(webElement)// 根据文本来选择
select.selectByVisibleText("选择框文本");Thread.sleep(3000);
driver.quit();
6.11.2 根据属性值选择
WebElement webElement = driver.findElement(By.cssSelector("#Method"));// 创建选择框对象(将此web元素封装为Select对象)
Select select = new Select(webElement)// 根据属性值来选择
select.selectByValue("选择框属性值");Thread.sleep(3000);
driver.quit();
6.11.3 根据序号选择
注意:select下拉框索引值从0开始!
WebElement webElement = driver.findElement(By.cssSelector("#Method"));// 创建选择框对象(将此web元素封装为Select对象)
Select select = new Select(webElement)// 根据序号来选择
select.selectByIndex(1);Thread.sleep(3000);
driver.quit();
6.12 文件上传
public void fileUploadController() throws InterruptedException {driver.get("");Thread.sleep(3000);driver.findElement(By.cssSelector("body > div > div > input[type=file]")).sendKeys("文件路径");Thread.sleep(3000);driver.quit();
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body>
<input type="file" id="fu" value="选择文件">
<br>
<input type="button" id="fw" value="上传">
</body>
</html>
package org.example;import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;import java.time.Duration;public class Main {public static void main(String[] args) throws InterruptedException {System.out.println("Hello world!");EdgeOptions edgeOptions = new EdgeOptions();edgeOptions.addArguments("--remote-allow-origins=*");EdgeDriver edgeDriver = new EdgeDriver(edgeOptions);edgeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));// 隐式等待edgeDriver.get("file:///D:/idea-workspace/WebTest/src/main/resources/web/fileUpload/fileUpload.html");Thread.sleep(3000);edgeDriver.findElement(By.cssSelector("#fu")).sendKeys("D:/idea-workspace/WebTest/src/main/resources/web/fileUpload/fileUpload.html");Thread.sleep(3000);edgeDriver.quit();}
}
注意:如果上传文件前端控件源码不是input类型,需要使用第三方工具,比如:autoit
6.13 时间日期控件
- 控件没有限制手动输入,则直接调用sendKeys方法写入时间数据。
- 控件有限制输入,则可以通过执行一段js脚本来改变元素的value属性值。
6.13.1 方式一
package org.example;import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.support.ui.Select;import java.time.Duration;public class Main {public static void main(String[] args) throws InterruptedException {EdgeOptions edgeOptions = new EdgeOptions();edgeOptions.addArguments("--remote-allow-origins=*");EdgeDriver edgeDriver = new EdgeDriver(edgeOptions);edgeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));// 隐式等待edgeDriver.get("https://www.fliggy.com/?ttid=seo.000000580&seoType=origin");edgeDriver.findElement(By.cssSelector("#J_FlightForm > fieldset > div:nth-child(5) > div > div > input")).sendKeys("2000-01-01");Thread.sleep(1000);edgeDriver.quit();}
}
6.13.2 方式二
见 6.13 执行脚本
6.13 执行脚本
方法 | 说明 |
---|---|
driver.executeScript() | 参数为要执行的js脚本 |
package org.example;import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.support.ui.Select;import java.time.Duration;public class Main {public static void main(String[] args) throws InterruptedException {EdgeOptions edgeOptions = new EdgeOptions();edgeOptions.addArguments("--remote-allow-origins=*");EdgeDriver edgeDriver = new EdgeDriver(edgeOptions);edgeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));// 隐式等待edgeDriver.get("https://www.12306.cn/index/");
// edgeDriver.findElement(By.id("train_date"));
// edgeDriver.executeScript("var v = train_date;\n" + "v.autocomplete = true");edgeDriver.executeScript("var v = train_date; v.autocomplete = true");edgeDriver.findElement(By.id("train_date")).clear();edgeDriver.findElement(By.id("train_date")).sendKeys("2000-01-01");Thread.sleep(1000);edgeDriver.quit();}
}
6.14 浏览器参数设置
EdgeOptions edgeOptions = new EdgeOptions();
edgeOptions.addArguments("--remote-allow-origins=*");
addArguments可接收参数 | 说明 |
---|---|
“–disable-gpu” | 禁用GPU加速 |
“–headless” | 在无头模式下运行浏览器,即不显示图形界面 |
“–incognito” | 在隐身模式下启动浏览器 |
“–disable-extensions” | 禁用浏览器扩展 |
“–disable-notifications” | 禁用浏览器通知 |
“–disable-popup-blocking” | 禁用弹出窗口拦截功能 |
“–start-maximized” | 启动时最大化浏览器 |
“–disable-infobars” | 禁用信息栏(例如Chrome正在受到自动测试软件控制) |
“–ignore-certificate-errors” | 忽略证书错误 |
“–proxy-server=http://hostname:port” | 设置代理服务器 |
注意:浏览器的参数设置需要在创建浏览器对象之前进行设置。
6.15 验证码
6.15.1 万能验证码(推荐)
后端代码添加测试通用验证码。
6.15.2 去除验证码
后端去除验证码。
6.15.3 自动识别(难度大)
可以付费调用自动识别API,不推荐。
6.16 退出
方法 | 说明 |
---|---|
driver.close() | 关闭当前窗口(如果是当前打开的最后一个窗口,则退出浏览器) |
driver.quit() | 退出此驱动程序,关闭每个相关窗口 |
在操作完毕后必须调用
quit()
进行释放资源,否则驱动将持续在内存中不会被释放掉。
7. 最后
码字不易,xdm,qiuqiu点个👍吧!