selenium - web 自动化测试

1.什么是自动化

有效的减少人力的消耗, 同时提高生活的质量. 通过自动化测试有效减少人力的投入, 同时提高了测试的质量和效率.

1.1 为什么需要自动化测试

测试人员有个环节叫做回归测试.

回归测试 : 对历史版本, 历史功能进行测试, 保证功能都是符合要求的.
为什么需要回归测试历史版本 ?
因为新版本和旧版本一般都是使用同一个代码库, 新增加的代码很可能影响原来的代码, 所以需要回归测试历史版本.

而当产品的历史版本越来越多的时候, 版本回归的压力越来越大时, 仅仅通过人工测试来回归所有的版本肯定是不现实的, 所以我们需要借助自动化来进行回归测试.

2. 自动化测试的分类

自动化测试分为两大类 : 接口自动化测试 + UI 自动化测试

UI 自动化测试又分两类 :

  • 移动端自动化测试

  • web 端自动化测试

3. selenium web 自动化测试工具

3.1 为什么选择 selenium 作为 web 自动化测试工具

1. selenium 开源免费
2. selenium 支持多浏览器. 例如 Chrome, Firefox, IE浏览器等等.
3. 支持多系统. 例如 Linux, Windows, MacOS.
4. 支持多语言. Java | Python | CSharp | Ruby | JavaScript | Katlin.
5. selenium 包提供了很多可供测试使用的 API.

3.2 selenium 环境部署

  1. 准备 Edge 浏览器 (其他浏览器也行)

  1. 准备 Edge 驱动包 (EdgeDriver)

a.查看自己的 edge 浏览器版本 (浏览器版本和驱动版本一定要对应)

b. 下载对应的驱动包, 下载路径 : https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/

步骤1

步骤2

注意步骤 2 中, 只要保持 111.0.1611 一致即可, 最后一位数字选择最新版本.

c. 解压下载好的 edge 驱动包, 找到 msedgedriver.exe, 将其放到 Java 系统环境变量下.

例如我的 : 将其放到 JDK 的 bin 目录下

  1. 准备 selenium 工具包

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.0.0</version>
</dependency>
  1. 准备 Java 8 以及 Java 8 以上

selenium 环境部署过程中报错, 可以根据此文档分析错误原因 : https://bitejiuyeke.feishu.cn/docx/doxcnnSojH440lAzg9wbqT0NLrg

3.3 selenium, driver, 浏览器三者的关系

驱动的请求地址 : 127.0.0.1 : 9515

步骤1

步骤2

4. 一个简单的自动化测试用例

  1. 创建 maven 项目

  1. 导入 selenium 包

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.0.0</version>
</dependency>
  1. 在 test/java/com 下自定义一个包

  1. 编写代码

自动化测试类 : 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. 结束会话

5.selenium 基础方法

5.1 selenium 常见元素的操作

5.1.1 查找页面元素

findElement()

参数: By 类 (提供通过什么方式来查找元素)
返回值 : webElement
  • 当元素可以在页面中找到, 程序正确退出

  • 当元素在页面中找不到时, 程序执行报错 >

NoSuchElementException

findElements()

参数 : By 类 (提供通过什么方式来查找元素)
返回值 : List<WebElement>

5.1.2 元素定位的方式 - By 类

元素定位的方式 (By 类后面跟的类容), 主要分为两种: selector 和 xpath
selector 又分为 基础选择器和复合选择器.

此处以 cssSelector 为例.

By.cssSelector("页面元素")

选择页面元素的步骤 :

F12 打开开发者工具, 然后点击小三角形.(然后点击输入框, 或者点击百度一下按钮)

步骤1

在右边, 变颜色的地方右键, 选择 Copy -> Copy selector

步骤2

然后复制粘贴到 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, 就表示唯一, 反之不唯一.

5.1.3 输入文本 sendKeys

仅适用于文本字段和内容可编辑的元素 (用于其他元素也不会报错)

两种方式 :

// 方式 1
WebElement ele = driver.findElement(By.cssSelector("#kw"));
ele.sendKeys("陈伟霆");
// 方式 2
driver.findElement(By.cssSelector("#kw")).sendKeys("陈伟霆");

5.1.4 点击 click

用于点击一个按钮.

driver.findElement(By.cssSelector("#su")).click();

5.1.5 提交 submit

submit 相当于通过回车键提交, 但是 selenium 官方不推荐使用 submit, 更推荐 click. 因为 submit 仅适用于表单元素, 而且能使用 submit 提交, 就一定能使用 click 代替, 而反过来不行.

5.1.6 清除 clear

使用场景不多, 适用于频繁的测试输入是否可以重复输入.

测试代码 :

// 输入框输错了, 清除重输
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);

5.1.7 获取文本 getText

示例 :

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();
}

运行结果 :

第一个是获取两个标签之间的文本, 第二个是获取百度一下按钮上的百度一下这几个字.

为什么第一个能获取到, 而第二个获取不到呢 ?

第二个获取不到的原因

因为第二个获取百度一下按钮上的百度一下, 不属于文本, 而是属性对应的值.

5.1.8 获取属性对应的值 getAttribute

如何获取属性对应的值 : (获取百度一下按钮上对应的所有属性)

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"));

运行结果 :

5.1.9 获取页面标题和URL

获取标题 : 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) 一下 (仅观察效果)

5.2 窗口

5.2.1 窗口大小的设置

最大化, 最小化, 全屏窗口, 手动设置窗口大小.

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();

5.2.2 窗口的切换

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<String> 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<String> 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() 即可.

5.2.3 屏幕截图

先导入保存屏幕截图文件需要用到的依赖

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

代码示例 : 去到百度首页, 输入陈伟霆后回车, 然后获取新页面的陈伟霆元素.

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 秒)

5.3 等待

为什么需要等待, 就是为了解决程序执行速度比浏览器渲染速度要快很多的问题.

四种等待 : 强制等待, 隐式等待, 显示等待, 流畅等待. (主要是前三种)

5.3.1 强制等待

Thread.sleep()

程序阻塞进行, 会用到, 但是自动化里面不用的特别多.
每一个自动化测试方法就是一个自动化测试用例, 而每个测试用例只能接受十几秒或者几分钟之内的等待.

5.3.2 隐式等待

针对屏幕截图遇到的问题, 添加隐式等待, 就可以正常获取到页面元素了.

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 就找到了, 就不会等待三秒, 直接就往下执行了; 如果不存在该元素, 就在设置好的时间里轮询等待即可, 时间一过, 元素还没找到, 程序执行就会报错.

5.3.3 显示等待

使用显示等待解决上述问题 :

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. 有时候隐式等待和显示等待不生效时 (因为前端页面渲染等诸多因素), 可以加上强制等待.

5.4 浏览器导航

浏览器的前进, 后退, 刷新操作.

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();
}

5.5 弹窗

弹窗的类型 : 警告弹窗, 确认弹窗, 提示弹窗.

警告弹窗

确认弹窗

提示弹窗

处理弹窗的步骤 :

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(), 代码也不会报错.

5.6 选择框

选择的选择方式 : 根据文本来选择, 根据属性值来选择, 根据序号来选择.

注意根据序号来选择的时候, 序号是从 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();
}

5.7 执行脚本

主要方法 : executeScript()

参数 : js 代码.

5.7.1 页面置顶

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();

5.8 文件上传

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();
}

上传文件后的效果 :

5.9 浏览器的参数设置

前面我们所学到的 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();
}
浏览器的参数设置需要在创建浏览器对象之前进行设置.

本篇博客就到这里了, 谢谢观看!!

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值