文章目录
- 1. 什么是自动化
- 2. 自动化测试的分类
- 3. selenium(web 自动化测试工具)
- 4. 一个简单的自动化例子
- 5. selenium 常用方法
- 5.1 查找页面元素 findElement ()
- 5.2 元素的定位 By 类
- 5.3 xpath 路径语言
- 6. 常见的元素操作
- 6.1 输入文本 sendKeys
- 6.2 点击 click
- 6.3 提交 submit (通过回车键提交)
- 6.4 请求 clear
- 6.5 获取文本 getText
- 6.6 获取属性对应的值 getAttribute
- 6.7 获取页面的标题和 URL
- 7. 窗口
- 7.1 窗口大小的设置(manage().window())
- 7.2 窗口的切换
- 8. 屏幕截图 getScreenshotAs
- 9. 等待
- 9.1 强制等待
- 9.2 隐式等待
- 9.3 显示等待
- 10. 浏览器导航 navigate
- 11. 弹窗
- 12. 选择框 select
- 13. 执行脚本 executeScript
- 14. 文件上传
- 15. 浏览器的参数设置 ChromeOptions
- 16. 单元测试工具 Junit
- 16.1 Junit 的执行顺序
- 16.2 参数化
- 16.3 测试套件
- 17. 断言 Assertions
谷歌浏览器升级到111版本之后,Java版本的selenium和Chrome不兼容
所以更换浏览器和驱动,使用edge浏览器和edge驱动,因为本篇是在 chrome 浏览器 111 版本之前写的,所以
在看到创建 ChromeDriver driver = new ChromeDriver(); 驱动对象的操作,请将这段代码替换为
EdgeOptions options = new EdgeOptions(); options.addArguments("-remote-allow-origins=*"); EdgeDriver driver = new EdgeDriver(options)
EdgeOptions
是用于设置 Edge 浏览器的选项的类;options.addArguments("-remote-allow-origins=*")
是设置 Edge 浏览器允许跨域请求的选项,即允许 WebDriver 与浏览器在不同的域之间通信。- 如果不使用 EdgeOptions 对象来设置,就无法实现这个功能。而 ChromeDriver 则没有类似的选项需要设置,所以可以直接使用 ChromeDriver driver = new ChromeDriver() 来创建驱动对象但因为版本不兼容就不用 chrome 了。
1. 什么是自动化
作用:通过自动化测试有效减少人力的投入,同时提高了测试的质量和效率
概念:自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程
比如,回归测试版本越来越多,版本回归的压力越来越大,仅通过人工测试来回归所有的版本肯定是不现实的,所以我们需要借助自动化测试
2. 自动化测试的分类
- 接口自动化测试
- UI自动化测试(界面测试)
- 移动端自动化测试
- web 端自动化测试
3. selenium(web 自动化测试工具)
(1)选择 selenium 作为的 web 自动化测试工具是因为
- 开源免费
- 支持多浏览器,比如 Chrome、Firefox、IE浏览器…
- 支持多系统,比如 Linux、Windows、MacOs
- 支持多语言,比如 Java、Python、JavaScript…
- selenium 包提供了很多可供测试使用的 API
(2)环境部署
如果想要使用 selenium 实施 Web 自动化测试,需要的环境是什么
Chrome 浏览器、谷歌驱动(ChromeDriver)、selenium 工具包
(3)什么是驱动?
人工测试的情况下,人来手动的打开浏览器,那么驱动就是人力
对自动化来说,代码不能够直接打开浏览器,需要借助驱动程序才能打开浏览器
selenium 编写的自动化脚本是如何打开浏览器(selenium Driver 浏览器三者之间的关系)
驱动要接受 selenium 脚本发送过来的 http 请求并解析
4. 一个简单的自动化例子
创建 Maven 项目,导入 selenium 包
<dependencies><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.0.0</version></dependency>
</dependencies>
简单自动化示例主要包含五个步骤
- 创建驱动实例,创建会话(实例化 ChromeDriver 对象)
- 访问网站(对象.get(“网站”))
- 查找元素(对象.findElement(By.cssSelector(“#网址元素id”)).sendKeys(“输入内容”))
- 操作元素(对象.findElement(By.cssSelector(“#网址元素id”)).click())
- 结束会话(对象.quit())
package com.autotest0113;import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;import java.util.List;public class FirstAutotest {// 在百度网址搜索关键词 “胡歌”public void huge() throws InterruptedException {// 1.打开浏览器ChromeDriver chromeDriver = new ChromeDriver();Thread.sleep(3000);// 2.在浏览器里输入百度网址,访问百度首页chromeDriver.get("https://www.baidu.com");Thread.sleep(3000);// 3.找到百度首页输入框元素,并输入关键词 “胡歌”chromeDriver.findElement(By.cssSelector("#kw")).sendKeys("胡歌");Thread.sleep(3000);// 4.找到百度首页 “百度一下” 按钮,并点击一下chromeDriver.findElement(By.cssSelector("#su")).click();Thread.sleep(3000);// 5.结束会话(关闭浏览器)chromeDriver.quit();}
}
package com.autotest0113;import sun.text.normalizer.CharTrie;public class RunAutoTest {public static void main(String[] args) throws InterruptedException {FirstAutotest firstAutotest = new FirstAutotest();firstAutotest.huge();}
}
5. selenium 常用方法
5.1 查找页面元素 findElement ()
参数:By 类(提供通过什么方式来查找元素)
返回值:webElemen
当元素可以在页面找到的情况下,程序正常退出
当元素在页面找不到的情况下,程序执行报错
findElements()方法
参数:By 类(提供通过什么方式来查找元素)
返回值:List
public void methodTest() {ChromeDriver driver = new ChromeDriver();driver.get("https://www.baidu.com");driver.findElement(By.cssSelector("#kw"));List<WebElement> elements= driver.findElements(By.className("hotsearch-item"));for (WebElement element : elements) {System.out.println(element.getText());}driver.findElement(By.cssSelector("#su"));driver.quit();
}
5.2 元素的定位 By 类
5.3 xpath 路径语言
语法:
层级:/子级 // 跳级
属性:@
函数:contains()…
自动化里要求元素的定位必须要唯一,但是手动在页面复制 selector 或者 path 元素不一定是唯一的,需要我们进行手动修改到唯一
6. 常见的元素操作
6.1 输入文本 sendKeys
sendKeys,仅适用于文本字段和内容可编辑的元素
// 找到百度搜索框WebElement ele = driver.findElement(By.cssSelector("#kw"));
// 在输入框输入文本
ele.sendKeys("qq音乐");// 找到百度搜索框并输入文本
// driver.findElement(By.cssSelector("#kw")).sendKeys("qq音乐");
6.2 点击 click
// 3.找到百度搜索框并输入文本
driver.findElement(By.cssSelector("#kw")).sendKeys("qq音乐");
6.3 提交 submit (通过回车键提交)
submit 仅适用于表单元素,selenium 官方不推荐使用 submit,更推荐 click
// 找到百度搜索框并输入文本
driver.findElement(By.cssSelector("#kw")).sendKeys("qq音乐");
// 找到百度一下通过回车键进行提交
driver.findElement(By.cssSelector("#su")).submit();
6.4 请求 clear
clear:适用于频繁测试输入是否可以重复输入
// 清除输入框的内容
driver.findElement(By.cssSelector("#kw")).clear();
6.5 获取文本 getText
// 获取文本
String text = driver.findElement(By.cssSelector("#s-top-left > div > a")).getText();
System.out.println("获取到的文本:" + text);
6.6 获取属性对应的值 getAttribute
// 获取属性的值
String buttonText = driver.findElement(By.cssSelector("#su")).getAttribute("type");
System.out.println("获取到的属性名:" + buttonText);
String buttonText1 = driver.findElement(By.cssSelector("#su")).getAttribute("id");
System.out.println("获取到的属性名:" + buttonText1);
String buttonText2 = driver.findElement(By.cssSelector("#su")).getAttribute("value");
System.out.println("获取到的属性名:" + buttonText2);
6.7 获取页面的标题和 URL
System.out.println(driver.getTitle());
System.out.println(driver.getCurrentUrl());
driver.findElement(By.cssSelector("#kw")).sendKeys("你好");
driver.findElement(By.cssSelector("#su")).click();
System.out.println(driver.getTitle());
System.out.println(driver.getCurrentUrl());
7. 窗口
7.1 窗口大小的设置(manage().window())
最大化、最小化、全屏窗口、手动设置窗口大小
public void windowConTrol() throws InterruptedException {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(2000);driver.quit();
}
7.2 窗口的切换
打开百度首页,点击图片超链接进入到百度图片首页,获取百度图片页面的 “百度一下” 按钮
public void windowConTrol() throws InterruptedException, IOException { // 窗口切换driver.get("https://www.baidu.com");driver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")).click();Thread.sleep(3000);driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));driver.quit();}
结果报错了,找不到页面元素
这是是因为,当浏览器每次打开一个标签页的时候,会自动的给每个标签页进行标识,这个叫做句柄
可以通过 getWindowHandles() 获取所有标签页的句柄 Set,然后再通过getWindowHandle() 获取当前页面的句柄,然后遍历所有标签页的句柄,如果当前页的句柄和遍历 Set 中的句柄不同,就通过 switchTo().window() 方法进行窗口切换
public void windowConTrol() throws InterruptedException, IOException {driver.get("https://www.baidu.com");driver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")).click();Thread.sleep(3000);// 获取当前页面的句柄String curHandle = driver.getWindowHandle();System.out.println("当前页面的句柄:" + curHandle);// 获取所有标签的句柄Set<String> handles = driver.getWindowHandles();for (String handle : handles) {if(handle != curHandle) {driver.switchTo().window(handle);}}driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));Thread.sleep(3000);driver.quit();}
那么当我们打开了好几个窗口,怎么切换?
自动化基本没有这样的场景,所以这里不用考虑太多
8. 屏幕截图 getScreenshotAs
这里需要在 pom.xml 中引入屏幕截图文件需要用到的包
<!-- 保存屏幕截图文件需要用到的包--><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>
需要注意的一点是,当代码执行到查找结果页面元素的时候,页面还没有加载完全,此时程序执行的速度是比,页面渲染的速度更快的,所以要注意在代码中添加等待机制,让页面先渲染一下,再截图
public void windowConTrol() throws InterruptedException, IOException {// 屏幕截图driver.get("https://www.baidu.com");driver.findElement(By.cssSelector("#kw")).sendKeys("网易云音乐");driver.findElement(By.cssSelector("#su")).click();Thread.sleep(3000);// 屏幕截图(保存现场)File srcfile = driver.getScreenshotAs(OutputType.FILE);// 把屏幕截图好的文件放到指定的路径下String filename = "my.png";FileUtils.copyFile(srcfile,new File(filename));driver.findElement(By.cssSelector("#content_left"));driver.quit();}
我这里的代码文件名为固定格式,如果程序多次执行生成的图片文件会被同名覆盖,这就可以将文件名的命名添加动态数据(时间戳)
9. 等待
程序执行的速度要比浏览器渲染的速度要快很多
四种等待:强制等待、隐式等待、显示等待、流畅等待
需要注意:显示等待、隐式等待,不能同时使用,同时使用可能会出现意想不到的等待结果
9.1 强制等待
在 8. 屏幕截图时,为了让页面渲染完全后才进 行屏幕截图,中间进行了等待 3s (Thread.sleep())
这个就会使 程序阻塞进行,这种方式在自动化测试中会用到,但用的不是特别多,这是因为
每一个自动化方法就是一个自动化测试用例,比如一个用例需要 10s,100测试用例 1000s,200个用例 2000s,这样耗费时间太多了,只能接受十几秒或几分钟内
9.2 隐式等待
隐式等待会作用于 driver 的整个生命周期
隐式等待会一直轮询判断元素是否存在,如果不存在就等待设置好的时间里不断的进行轮询,直到元素能够被找到
public class autoTest {ChromeDriver driver = new ChromeDriver();public void waitContro() {// 添加隐式等待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("#content_left"));driver.quit();}
}
9.3 显示等待
public class autoTest {ChromeDriver driver = new ChromeDriver();public void webDriverWait() {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("#content_left")));driver.quit();}
}
10. 浏览器导航 navigate
浏览器的前进(forward)、后退(back)、刷新(refresh)
public class autoTest {ChromeDriver driver = new ChromeDriver();// 浏览器导航public void navigateConTrol() {driver.get("https://www.baidu.com");
// driver.navigate().to("https://www.baidu.com");// 想要回退到访问百度网址之前的状态driver.navigate().back();// 前进 进入到百度首页driver.navigate().forward();// 刷新百度首页driver.navigate().refresh();driver.quit();}
}
11. 弹窗
弹窗的类型:警告弹窗、确认弹窗、提示弹窗
处理弹窗的步骤:
- 将 driver 对象作用到弹窗上(切换到弹窗) driver.switchTo.alert()
- 选择确认 accept() / 取消 dismiss() (提示弹窗 输入文本 sendkeys 在页面上是看不到输入文本的执行效果的)
虽然警告弹窗只有确认按钮,但 accept 和 dismiss 都能处理
虽然警告弹窗和确认弹窗都没有输入文本的地方,但如果要执行 alert.sendKeys 代码,也是不会报错的
ChromeDriver chromeDriver = new ChromeDriver();
// 提示弹窗
void Alertcontro() throws InterruptedException {chromeDriver.get("file:///D:/%E6%8F%90%E7%A4%BA%E5%BC%B9%E7%AA%97.html");Thread.sleep(3000);// 打开弹窗chromeDriver.findElement(By.cssSelector("body > input[type=button]")).click();Thread.sleep(3000);// 切换到弹窗进行弹窗处理Alert alert = chromeDriver.switchTo().alert();Thread.sleep(3000);// 1. 输入alert.sendKeys("666");Thread.sleep(3000);// 2. 点击确认alert.accept();Thread.sleep(3000);// 取消alert.dismiss();chromeDriver.quit();
}
12. 选择框 select
选项的选择方式:根据文本选择(selectByVisibleText)、根据属性值选择(selectByValue)、根据序号选择(selectByIndex)
ChromeDriver chromeDriver = new ChromeDriver();
void selectControll() throws InterruptedException {chromeDriver.get("file:///D:/%E9%80%89%E6%8B%A9%E6%A1%86.html");WebElement ele = chromeDriver.findElement(By.cssSelector("#ShippingMethod"));Thread.sleep(2000);// 先创建选择框对象Select select = new Select(ele);Thread.sleep(2000);// 根据文本来选择
// select.selectByVisibleText("333");// 根据属性值选择
// select.selectByValue("222");// 根据序号选择【序号从 0 开始】select.selectByIndex(3);Thread.sleep(2000);chromeDriver.quit();}
13. 执行脚本 executeScript
executeScript(参数:JS)
ChromeDriver chromeDriver = new ChromeDriver();
void scriptControll() throws InterruptedException {chromeDriver.get("https://image.baidu.com/");Thread.sleep(3000);// 执行 JS 命令:让页面置顶/置底chromeDriver.executeScript("document.documentElement.scrollTop=500");Thread.sleep(3000);chromeDriver.executeScript("document.documentElement.scrollTop=0");Thread.sleep(3000);chromeDriver.quit();
}// 也可以通过 JS 代码来操作在输入框中搜索
void scriptControll() throws InterruptedException {chromeDriver.get("https://www.baidu.com");// 找到百度搜索框,输入 “迪丽热巴”chromeDriver.executeScript("var ss = document.querySelector(\"#kw\");ss.value = '迪丽热巴';");Thread.sleep(3000);chromeDriver.quit();
}
14. 文件上传
ChromeDriver driver = new ChromeDriver();
// 文件上传
void fileUploadControll() throws InterruptedException {driver.get("file:///D:/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0.html");driver.findElement(By.cssSelector("#avatar")).sendKeys("D:\\微信图片_20230212212832.jpg");Thread.sleep(3000);driver.quit();
}
15. 浏览器的参数设置 ChromeOptions
无头模式:在实际工作中,测试人员将自动化部署在机器上自动的执行,测试人员看不到测试过程,而是直接查看自动化执行的结果
有头模式:就是程序在自动的测试,我们是可以看到执行测试的过程界面
浏览器的参数设置需要在创建浏览器对象之前
void paramsControll() {// 先创建选项对象,然后再设置浏览器参数ChromeOptions options = new ChromeOptions();options.addArguments("-headless");// 百度搜索迪丽热巴ChromeDriver driver = new ChromeDriver(options);driver.get("https://www.baidu.com");driver.findElement(By.cssSelector("#kw")).sendKeys("迪丽热巴");driver.findElement(By.cssSelector("#su")).click();driver.quit();
}
16. 单元测试工具 Junit
自动化就是 selenium 脚本来实现的,junit 是 java 的单元测试工具,只不过在实现自动化的时候需要借用一下 junit 库里面提供的一些注解(junit5)
<dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-suite</artifactId><version>1.8.2</version><scope>test</scope>
</dependency>
@Test(测试方法)、@BeforeEach(每个测试用例前执行一次)、@BeforeAll(所有测试用例前执行一次)、 @AfterEach(每个测试用例之后执行一次)、@AfterAll(所有测试用例之后执行一次)
并且 BeforeAll 和 AfterAll 注解的方法必须为静态方法
(1)注解
junit 中提供了注解功能
@Test 表示方法是测试方法,执行当前这个类时,会自动的执行该类下所有带 @Test 注解的用例
public class junitTest {@Testvoid bbb() { System.out.println("bbbb");}@Testvoid ccc() { System.out.println("cccc");}
}
@BeforeEach 表示当前的方法需要在每个用例执行之前都执行一次
public class junitTest {@BeforeEachvoid aaa() {System.out.println("aaaa");}@Testvoid bbb() {System.out.println("bbbb");}@Testvoid ccc() {System.out.println("cccc");}
}
@BeforeAll 当前的方法需要在当前类下所有用例之前执行一下,被该注解修饰的方法必须为静态方法
public class junitTest {@BeforeAllstatic void aaa() {System.out.println("aaaa");}@Testvoid bbb() {System.out.println("bbbb");}@Testvoid ccc() {System.out.println("cccc");}
}
@AfterEach 在每个用例执行之后都要执行一次
public class junitTest {@AfterEachvoid aaa() {System.out.println("aaaa");}@Testvoid bbb() {System.out.println("bbbb");}@Testvoid ccc() {System.out.println("cccc");}
}
@AfterAll
public class junitTest {@AfterAllstatic void aaa() {System.out.println("aaaa");}@Testvoid bbb() {System.out.println("bbbb");}@Testvoid ccc() {System.out.println("cccc");}
}
16.1 Junit 的执行顺序
注意看两次代码的执行顺序
可以看到测试用例的执行并不会按照我们编写测试用例的顺序来执行,那么这就会出现一些问题
比如说对博客的登录系统编写测试用例,用例包含:
- 检查登录页面展现是否正确
- 检查正常登录 (跳转到博客首页 / 列表页)
- 异常登录
执行顺序的问题,就会导致比如说,先执行步骤二,那么此时是登录成功的,页面就会跳转,然后再执行步骤一就会 报错
1. 通过 order 注解来排序
- 先使用注解说明当前类下所有的用例需要使用 order 注解来进行排序,注解该注解必须要使用到类上
- 然后通过 Order 来指定用的执行具体顺序
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JunitTest {@Test@Order(1)void ediloginTest() {System.out.println("loginTest");}@Test@Order(2)void AindexTest() {System.out.println("indexTest");}@Test@Order(3)void editTest() {System.out.println("editTest");}
}
16.2 参数化
尽可能的通过一个用例,多组参数来模拟用户的行为
单参数:
@ParameterzedTest + @ValueSource(数据类型方法/加s = {参数1,参数2,参数3,…})
多参数:
@parameterizedTest + @CsvSource({“”, “”, '", …}) 每个双引号就是一组测试用例
@ParameterizedTest
@CsvSource({"张三,22","李四,25","王五,35"})
void muchParamsTest(String name,int age) {System.out.println("name: " + name + ",age: " + age);
}
多参数(从第三方 csv 文件读取数据源)
@ParameterizedTest + @CsvFileSource(files = “文件路径+名字”)
这里的这个 csv 文件,不要直接改后缀生成,要用系统自带的 Excel 工具,来打开和编辑 csv 文件
@ParameterizedTest
@CsvFileSource(files = "D:\\mycsv.csv")
void csvfileParamsTest(String name, int age) {System.out.println("name: " + name + ",age: " + age);
}
driver.navigate().back();
这个是模拟了浏览器的后退按钮,使得当前页面返回到上一个页面。这个方法可以用于测试中,例如在测试过程中需要回到之前的页面重新执行某些操作或者验证某些信息。
动态参数方法
// 通过动态方法来提供数据源
@ParameterizedTest
@MethodSource("methodParams")
void dynamicMethodParamsTest(String name,int age) {System.out.println("name: " + name + ",age: " + age);
}
static Stream<Arguments> methodParams() throws InterruptedException {// 构造动态参数String[] arr = new String[5];for (int i = 0; i < arr.length; i++) {Thread.sleep(500);arr[i] = System.currentTimeMillis() + "";}return Stream.of(Arguments.arguments(arr[0],20),Arguments.arguments(arr[1],20),Arguments.arguments(arr[2],20),Arguments.arguments(arr[3],20),Arguments.arguments(arr[4],20));
}
16.3 测试套件
创建三个类,给每个类中都加上测试注解 @Test,如果要测试运行,只能一个类一个类运行,不能对三个类同时运行,
所以可以使用测试套件来同时运行多个测试类
创建一个类,通过 @Suite 注解标识该类为测试套件类(而不是测试类)
- 指定类来运行用例(要运行的用例必须要被 @Test 注解,除了参数化的用例)
/*** @Description: 测试套件* @Date 2023/3/7 21:29*/
@Suite
@SelectClasses({aaa.class,bbb.class,ccc.class})
public class runSuite {
}
运行程序可以看到
- 指定包名来运行(第一种方法,如果类特别多的情况下,不太适合,可以指定包)
@Suite
@SelectPackages("com.autoTest0303")
public class runSuite {
}
17. 断言 Assertions
断言匹配 assertEquals,断言不匹配 assertNotEquals
@Test
void huixiang() {ChromeDriver driver = new ChromeDriver();driver.get("https://www.baidu.com");String text = driver.findElement(By.cssSelector("#su")).getAttribute("value");//百度一下// 假如这里获取的属性不是百度一下,而是百度两下System.out.println(text);Assertions.assertEquals("百度两下",text);driver.quit();
}
断言结果为真 assertTrue,断言结果为假 assertFalse
@Test
void ddd() {Assertions.assertTrue(1==1);//正确
}
@Test
void eee() {Assertions.assertFalse(1==1);//报错
}
@Test
void fff() {Assertions.assertFalse(1==0);//正确
}
断言结构为空 assertNull,断言结果不为空 assertNotNull
@Test
void ggg() {String aaa = null;Assertions.assertNull(aaa);//通过
}
@Test
void hhh() {Assertions.assertNotNull("你好");//通过
}