有效的减少人力的消耗, 同时提高生活的质量. 通过自动化测试有效减少人力的投入, 同时提高了测试的质量和效率.
测试人员有个环节叫做回归测试.
回归测试 : 对历史版本, 历史功能进行测试, 保证功能都是符合要求的.
为什么需要回归测试历史版本 ?
因为新版本和旧版本一般都是使用同一个代码库, 新增加的代码很可能影响原来的代码, 所以需要回归测试历史版本.
而当产品的历史版本越来越多的时候, 版本回归的压力越来越大时, 仅仅通过人工测试来回归所有的版本肯定是不现实的, 所以我们需要借助自动化来进行回归测试.
自动化测试分为两大类 : 接口自动化测试 + UI 自动化测试
UI 自动化测试又分两类 :
移动端自动化测试
web 端自动化测试
1. selenium 开源免费
2. selenium 支持多浏览器. 例如 Chrome, Firefox, IE浏览器等等.
3. 支持多系统. 例如 Linux, Windows, MacOS.
4. 支持多语言. Java | Python | CSharp | Ruby | JavaScript | Katlin.
5. selenium 包提供了很多可供测试使用的 API.
准备 Edge 浏览器 (其他浏览器也行)
准备 Edge 驱动包 (EdgeDriver)
a.查看自己的 edge 浏览器版本 (浏览器版本和驱动版本一定要对应)
b. 下载对应的驱动包, 下载路径 : https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/
注意步骤 2 中, 只要保持 111.0.1611 一致即可, 最后一位数字选择最新版本.
c. 解压下载好的 edge 驱动包, 找到 msedgedriver.exe, 将其放到 Java 系统环境变量下.
例如我的 : 将其放到 JDK 的 bin 目录下
准备 selenium 工具包
org.seleniumhq.selenium
selenium-java
4.0.0
准备 Java 8 以及 Java 8 以上
selenium 环境部署过程中报错, 可以根据此文档分析错误原因 : https://bitejiuyeke.feishu.cn/docx/doxcnnSojH440lAzg9wbqT0NLrg
驱动的请求地址 : 127.0.0.1 : 9515
创建 maven 项目
导入 selenium 包
org.seleniumhq.selenium
selenium-java
4.0.0
在 test/java/com 下自定义一个包
编写代码
自动化测试类 : FirstAutoTest.java
public class FirstAutoTest {
// 百度网址搜索陈伟霆
public void chenWeiTingTest() throws InterruptedException {
// 创建驱动实例, 创建会话(打开浏览器)
EdgeDriver driver = new EdgeDriver();
Thread.sleep(3000);
// 在浏览器找到百度网址, 访问百度首页
driver.get("https://www.baidu.com");
Thread.sleep(1000);
// 找到百度首页输入框元素, 并输入关键词 "陈伟霆"
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
Thread.sleep(1000);
// 找到百度首页的 "百度一下" 按钮, 并点击一下
driver.findElement(By.cssSelector("#su")).click();
Thread.sleep(3000);
// 结束会话, 关闭浏览器
driver.quit();
}
}
运行测试类的主类 : RunAutoTest.java
public class RunAutoTest {
public static void main(String[] args) throws InterruptedException {
FirstAutoTest firstAutoTest = new FirstAutoTest();
firstAutoTest.chenWeiTingTest();
}
}
这个代码运行起来, 就会自动打开 edge 浏览器, 然后找到百度首页, 然后在搜索栏输入陈伟霆, 然后自动回车, 然后自动关闭浏览器.
简单自动化示例主要包含 5 个步骤
1. 创建驱动实例, 创建会话.
2. 访问网站
3. 查找元素
4. 操作元素
5. 结束会话
findElement()
参数: By 类 (提供通过什么方式来查找元素)
返回值 : webElement
当元素可以在页面中找到, 程序正确退出
当元素在页面中找不到时, 程序执行报错 >
findElements()
参数 : By 类 (提供通过什么方式来查找元素)
返回值 : List
元素定位的方式 (By 类后面跟的类容), 主要分为两种: selector 和 xpath
selector 又分为 基础选择器和复合选择器.
此处以 cssSelector 为例.
By.cssSelector("页面元素")
选择页面元素的步骤 :
F12 打开开发者工具, 然后点击小三角形.(然后点击输入框, 或者点击百度一下按钮)
在右边, 变颜色的地方右键, 选择 Copy -> Copy selector
然后复制粘贴到 By.cssSelector("") 的引号里面即可.
xpath 语法
层级 : / 代表子集, // 代表跳级
属性 : @
函数 : contains()...
还是按照前面查找页面元素的方式, 改成复制 Copy xpath 即可, 例如如下两个 xpath :
/html/body/div[3]/div[3]/div/div[1]/div/div[3]/div[2]/div/ol[1]/li[1]/span[2]/a
//*[@id="su"]
第一个 xpath 就是找寻 html 标签下紧邻的 body 标签下紧邻的第三个 div 标签下......的 a 标签.
第二个 xpath 就是跳着找到 id = "su" 的元素.
【问题】为什么要了解 xpath 的语法 ?
因为自动化测试要求元素的定位必须要唯一. 但是手动在页面复制的 selector 或者 xpath 并不一定唯一, 需要我们手动的进行小范围的修改.
【问题】如何确定一个 xpath 在页面中是否唯一 ?
1. F12 打开开发者工具
2. Ctrl + f 就会弹出一个搜索框, 粘贴 xpath, 如果显示 1, 就表示唯一, 反之不唯一.
仅适用于文本字段和内容可编辑的元素 (用于其他元素也不会报错)
两种方式 :
// 方式 1
WebElement ele = driver.findElement(By.cssSelector("#kw"));
ele.sendKeys("陈伟霆");
// 方式 2
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
用于点击一个按钮.
driver.findElement(By.cssSelector("#su")).click();
submit 相当于通过回车键提交, 但是 selenium 官方不推荐使用 submit, 更推荐 click. 因为 submit 仅适用于表单元素, 而且能使用 submit 提交, 就一定能使用 click 代替, 而反过来不行.
使用场景不多, 适用于频繁的测试输入是否可以重复输入.
测试代码 :
// 输入框输错了, 清除重输
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com/");
driver.findElement(By.cssSelector("#kw")).sendKeys("你号");
Thread.sleep(1000);
driver.findElement(By.cssSelector("#kw")).clear();
driver.findElement(By.cssSelector("#kw")).sendKeys("你好");
driver.findElement(By.cssSelector("#su")).click();
Thread.sleep(2000);
示例 :
public void baseController() {
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com");
// 获取文本, 能正常获取到
String text = driver.findElement(By.cssSelector("#hotsearch-content-wrapper > li:nth-child(1) > a > span.title-content-title")).getText();
System.out.println("获取文本的结果 : " + text);
// 获取属性对应的值, 不能获取到
String value = driver.findElement(By.cssSelector("#su")).getText();
System.out.println("获取属性对应的值的结果 : " + value);
driver.quit();
}
运行结果 :
第一个是获取两个标签之间的文本, 第二个是获取百度一下按钮上的百度一下这几个字.
为什么第一个能获取到, 而第二个获取不到呢 ?
因为第二个获取百度一下按钮上的百度一下, 不属于文本, 而是属性对应的值.
如何获取属性对应的值 : (获取百度一下按钮上对应的所有属性)
System.out.println(driver.findElement(By.cssSelector("#su")).getAttribute("type"));
System.out.println(driver.findElement(By.cssSelector("#su")).getAttribute("id"));
System.out.println(driver.findElement(By.cssSelector("#su")).getAttribute("value"));
System.out.println(driver.findElement(By.cssSelector("#su")).getAttribute("class"));
运行结果 :
获取标题 : driver.getTitle()
获取URL : driver.getCurrentUrl()
代码示例 :
System.out.println("搜索之前 : ");
// 获取当前页面标题
System.out.println(driver.getTitle());
// 获取当前页面 URL
System.out.println(driver.getCurrentUrl());
driver.findElement(By.cssSelector("#kw")).sendKeys("你好");
driver.findElement(By.cssSelector("#su")).click();
System.out.println("搜索之后 : ");
System.out.println(driver.getTitle());
System.out.println(driver.getCurrentUrl());
运行结果 :
访问百度页面后, 获取当前 URL 和 当前页面的标题, 以及搜索你好zhi, 再次获取当前 URL 和当前页面的标题后, 为什么只是 URL 变了, 而页面标题没变呢 ?
因为输入你好之后, 页面还没来得渲染, 就获取页面标题, 导致获取的标题还是原来的标题, 可以在第二次获取之前 sleep(2000) 一下 (仅观察效果)
最大化, 最小化, 全屏窗口, 手动设置窗口大小.
EdgeDriver driver = new EdgeDriver();
Thread.sleep(3000);
// 窗口最大化
driver.manage().window().maximize();
Thread.sleep(3000);
// 窗口最小化
driver.manage().window().minimize();
Thread.sleep(3000);
// 全屏
driver.manage().window().fullscreen();
Thread.sleep(3000);
// 手动设置窗口大小
driver.manage().window().setSize(new Dimension(1024, 768));
Thread.sleep(3000);
// 关闭
driver.quit();
EdgeDriver driver = new EdgeDriver();
// 去到百度首页
driver.get("https://www.baidu.com");
// 点击百度上面的图片
driver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")).click();
Thread.sleep(2000);
// 获取图片页的 "百度一下" 按钮
driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));
Thread.sleep(2000);
driver.quit();
运行结果 : 发现百度按钮找不到
报错的原因 :
当浏览器每次打开一个标签页的时候, 会自动的给每个标签页进行标识, 这个标识我们称之为 "句柄" , 如果直接获取 , 浏览器就不知道是获取哪个页面的百度一下按钮.
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")).click();
// 获取当前页面的句柄
String curHandle = driver.getWindowHandle();
System.out.println("当前句柄 : " +curHandle);
// 获取所有页面的句柄
Set handles = driver.getWindowHandles();
for(String handle : handles) {
System.out.println(handle);
}
运行结果 :
如果想要获取图片页面的百度一下按钮, 该怎么写代码 ?
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")).click();
// 获取当前页面的句柄
String curHandle = driver.getWindowHandle();
System.out.println("当前句柄 : " +curHandle);
Set handles = driver.getWindowHandles();
for(String handle : handles) {
// 如果不是当前页面的句柄, 就切换窗口
if(handle != curHandle) {
driver.switchTo().window(handle);
}
}
Thread.sleep(2000);
// 获取图片页面百度一下按钮
driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));
Thread.sleep(2000);
driver.quit();
如果打开了好几个窗口, 我们该怎么切换 ?
自动化基本没有这样的场景, 如果有, 简单使用 driver.get() 即可.
先导入保存屏幕截图文件需要用到的依赖
commons-io
commons-io
2.6
代码示例 : 去到百度首页, 输入陈伟霆后回车, 然后获取新页面的陈伟霆元素.
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
driver.findElement(By.cssSelector("#su")).click();
// 获取新打开页面的元素
driver.findElement(By.cssSelector("#\\31 > div > div > div > div > div.cos-row.row-text_Johh7.row_5y9Az > div > a > div > p > span > span"));
// 退出
driver.quit();
运行结果 :
为什么会找不到元素 ?
修改代码, 在查找元素之前保存一下跳转后的页面截图 (保存现场)
EdgeDriver driver = new EdgeDriver();
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
driver.findElement(By.cssSelector("#su")).click();
// 屏幕截图 (保存现场)
File srcFile = driver.getScreenshotAs(OutputType.FILE);
String filename = "my.png";
FileUtils.copyFile(srcFile, new File(filename));
// 获取新打开页面的元素
driver.findElement(By.cssSelector("#\\31 > div > div > div > div > div.cos-row.row-text_Johh7.row_5y9Az > div > a > div > p > span > span"));
// 退出
driver.quit();
查看 my.png : 此时发现找不到元素的原因就是页面还没来得及渲染. (程序执行的速度远大于页面渲染的速度)
要想正常获取到页面元素, 就需要加入等待机制. (在保存屏幕之前, 等待 3 秒)
为什么需要等待, 就是为了解决程序执行速度比浏览器渲染速度要快很多的问题.
四种等待 : 强制等待, 隐式等待, 显示等待, 流畅等待. (主要是前三种)
Thread.sleep()
程序阻塞进行, 会用到, 但是自动化里面不用的特别多.
每一个自动化测试方法就是一个自动化测试用例, 而每个测试用例只能接受十几秒或者几分钟之内的等待.
针对屏幕截图遇到的问题, 添加隐式等待, 就可以正常获取到页面元素了.
EdgeDriver driver = new EdgeDriver();
// 添加隐式等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
driver.findElement(By.cssSelector("#su")).click();
// 获取新打开页面的元素
driver.findElement(By.cssSelector("#\\31 > div > div > div > div > div.cos-row.row-text_Johh7.row_5y9Az > div > a > div > p > span > span"));
// 退出
driver.quit();
隐式等待会作用于 driver 的整个生命周期. 虽然代码写在前面, 但是隐式等待会一直轮询判断元素是否存在, 如果元素存在, 并且在 1ms 就找到了, 就不会等待三秒, 直接就往下执行了; 如果不存在该元素, 就在设置好的时间里轮询等待即可, 时间一过, 元素还没找到, 程序执行就会报错.
使用显示等待解决上述问题 :
private EdgeDriver driver = new EdgeDriver();
public void windowController() throws InterruptedException, IOException {
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
driver.findElement(By.cssSelector("#su")).click();
// 添加显示等待
new WebDriverWait(driver, Duration.ofSeconds(5))
.until(driver->driver.findElement(By.cssSelector("#\\31 > div > div > div > div > div.cos-row.row-text_Johh7.row_5y9Az > div > a > div > p > span > span")));
driver.quit();
}
【注意】
1. 隐式等待和强制等待不能同时使用, 同时使用可能会出现意想不到的等待结果.
2. 有时候隐式等待和显示等待不生效时 (因为前端页面渲染等诸多因素), 可以加上强制等待.
浏览器的前进, 后退, 刷新操作.
private EdgeDriver driver = new EdgeDriver();
public void navigateController() throws InterruptedException, IOException {
// 请求百度首页的写法是简写
//driver.get("https://www.baidu.com");
driver.navigate().to("https//www.baidu.com");
Thread.sleep(3000);
// 回退
driver.navigate().back();
Thread.sleep(3000);
// 前进
driver.navigate().forward();
Thread.sleep(3000);
// 刷新
driver.navigate().refresh();
Thread.sleep(3000);
driver.quit();
}
弹窗的类型 : 警告弹窗, 确认弹窗, 提示弹窗.
警告弹窗
确认弹窗
提示弹窗
处理弹窗的步骤 :
1. 将 driver 对象作用到弹窗上 (切换到弹窗)
2. 点击确认 / 点击取消/提示弹窗输入文本, 再确认/取消
代码示例 :
private EdgeDriver driver = new EdgeDriver();
public void alertController() throws InterruptedException {
// 本地打开 html 文件后, 需要复制浏览器的 url 到 get 里, 而不是本地文件的路径
driver.get("file:///D:/bit-test/selenium-html/Prompt.html");
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();
}
1. 警告弹窗中, 切换到弹窗,调用 accept().
2. 确认弹窗中, 切换到弹窗, 调用 accept() 或 dismiss().
3. 提示弹窗中, 切换到弹窗, 可以先输入文本, 调用 sendKeys(), 然后调用 accept 或者 dismiss.
但是 sendKeys() 在页面上看不到输入文本的执行效果
注意 :
虽然警告弹窗只有确认按钮, 但是 accept 和 dismiss 都能处理. 虽然警告弹窗和确认弹窗都没有输入文本的地方, 但如果要执行 alert.sendKKeys(), 代码也不会报错.
选择的选择方式 : 根据文本来选择, 根据属性值来选择, 根据序号来选择.
注意根据序号来选择的时候, 序号是从 0 开始的.
代码示例 :
public void selectController() throws InterruptedException {
driver.get("file:///D:/bit-test/selenium-html/select.html");
Thread.sleep(3000);
WebElement ele = driver.findElement(By.cssSelector("#ShippingMethod"));
// 先创建选择框对象
Select select = new Select(ele);
// 根据文本来选择
// select.selectByVisibleText("UPS Next Day Air ==> $12.51");
// 根据属性值来选择
// select.selectByValue("12.51");
// 根据序号来选择 (序号从 0 开始)
select.selectByIndex(1);
Thread.sleep(3000);
driver.quit();
}
主要方法 : executeScript()
参数 : js 代码.
public void scriptController() throws InterruptedException {
driver.get("https://image.baidu.com/");
Thread.sleep(3000);
// 执行 js 命令: 让页面置顶/置底
driver.executeScript("document.documentElement.scrollTop=500");
Thread.sleep(3000);
// 0 就是顶部
driver.executeScript("document.documentElement.scrollTop=0");
Thread.sleep(3000);
driver.quit();
}
其他 js 操作 :
driver.get("https://www.baidu.com/");
Thread.sleep(3000);
// 执行 js 命令
driver.executeScript("var texts = document.querySelector('#kw'); texts.value = 'abcdef'");
Thread.sleep(3000);
driver.quit();
upload.html 页面效果 :
代码示例 :
public void fileUploadController() throws InterruptedException {
driver.get("file:///D:/bit-test/selenium-html/upload.html");
Thread.sleep(3000);
// 文件上传
driver.findElement(By.cssSelector("body > div > div > input[type=file]"))
.sendKeys("D:\\bit-test\\selenium-html\\upload.html");
Thread.sleep(3000);
driver.quit();
}
上传文件后的效果 :
前面我们所学到的 selenium 自动化测试, 它的行为都是打开浏览器, 然后再去访问链接, 然后再对页面进行一些操作, 这些只是为了让我们看到效果. 但是实际工作中, 是没有人会去关注它的效果的, 而是直接查看自动化执行的结果.
前面的操作都是可以看见浏览器的各种行为的, 叫做有头模式(看得见/前台). 而接下来就是设置无头模式(看不见/后台).
public void noHeadController() {
// 先创建选项对象, 然后再设置浏览器参数 (无头模式)
EdgeOptions options = new EdgeOptions();
options.addArguments("-headless");
EdgeDriver driver = new EdgeDriver(options);
// 访问百度首页, 搜索xxx, 回车
driver.get("https://www.baidu.com");
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");
driver.findElement(By.cssSelector("#su")).click();
driver.quit();
}
浏览器的参数设置需要在创建浏览器对象之前进行设置.
本篇博客就到这里了, 谢谢观看!!