写在前:hello这里是西西~ 这边博客主要学习关于自动化测试的相关内容,首先了解自动化测试 的相关理论知识,其次学习web应用中基于UI的自动化测试框架 - selemium[需要重点掌握selenium工作原理],实操selenium,最后学习Junit相关知识,开始学习之旅吧!
目录
[1.1 什么是自动化测试](#1.1 什么是自动化测试)
[1.2 自动化测试的分类](#1.2 自动化测试的分类)
[1.2.1 单元测试](#1.2.1 单元测试)
[1.2.2 接口测试](#1.2.2 接口测试)
[1.2.3 UI测试](#1.2.3 UI测试)
[1.3 如何实施自动化测试](#1.3 如何实施自动化测试)
[2. Selenium和Junit](#2. Selenium和Junit)
[2.1 Selenium](#2.1 Selenium)
[2.1.1 是什么](#2.1.1 是什么)
[2.1.2 特点](#2.1.2 特点)
[2.1.3 工作原理](#2.1.3 工作原理)
[2.2 Selenium + Java环境搭建](#2.2 Selenium + Java环境搭建)
[2.2.1 下载Chrome浏览器](#2.2.1 下载Chrome浏览器)
[2.2.2 查看Chrome版本](#2.2.2 查看Chrome版本)
[2.2.3 下载浏览器驱动](#2.2.3 下载浏览器驱动)
[2.2.4 配置环境变量PATH](#2.2.4 配置环境变量PATH)
[2.2.5 验证环境搭建是否成功](#2.2.5 验证环境搭建是否成功)
[2.3 Selenium实操](#2.3 Selenium实操)
[2.3.1 元素定位](#2.3.1 元素定位)
[2.3.2 XPath定位](#2.3.2 XPath定位)
[2.3.3 操作测试对象](#2.3.3 操作测试对象)
[(3)clear 清楚对象输入的文本内容](#(3)clear 清楚对象输入的文本内容)
[(4)submit 提交](#(4)submit 提交)
[(5)text 用于获取元素的文本信息](#(5)text 用于获取元素的文本信息)
[2.3.4 添加等待](#2.3.4 添加等待)
[2.3.5 打印信息](#2.3.5 打印信息)
[2.3.6 浏览器操作](#2.3.6 浏览器操作)
[2.3.7 键盘事件](#2.3.7 键盘事件)
[2.3.8 鼠标事件](#2.3.8 鼠标事件)
[2.3.9 定位一组元素](#2.3.9 定位一组元素)
[2.3.10 多层框架 / 窗口定位](#2.3.10 多层框架 / 窗口定位)
[2.3.11 下拉框处理](#2.3.11 下拉框处理)
[2.3.12 alert](#2.3.12 alert)
[2.3.13 上传文件](#2.3.13 上传文件)
[2.3.14 补充](#2.3.14 补充)
[2.4 Junit](#2.4 Junit)
[2.4.1 注解](#2.4.1 注解)
[2.4.2 参数化](#2.4.2 参数化)
[2.4.3 断言 assert](#2.4.3 断言 assert)
[2.4.4 测试套件 suite](#2.4.4 测试套件 suite)
1.自动化测试相关理论
1.1 什么是自动化测试
自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常条件和异常条件,最后评估运行结果。
将人工测试手段进行转换,让代码执行的过程。
1.2 自动化测试的分类
自动化测试包括UI自动化、接口自动化、单元测试自动化;
按照下面的金字塔模型来进行自动化测试规划,可以产生最佳自动化测试产出投入比(ROI),可以用较少的投入获得很好的收益。
1.2.1 单元测试
最大的投入应该改在单元测试上,单元测试的运行频率也更高;
java单元测试框架是Junit,后面会重点介绍。
1.2.2 接口测试
接口测试就是API测试,相对于UI自动化,API自动化更加容易实现,执行起来也更稳定。
接口测试自动化有如下特点:
- 可在产品前期,接口完成后介入;
- 用户维护量小;
- 适合于接口变动小、界面变动频繁的项目;
常见的接口自动化测试工具有:RobotFramework,JMeter、SoapUI、TestNG+HttpClient、Postman等。
1.2.3 UI测试
UI自动化更加贴近用户的需求和软件系统的实际业务,有时不得不进行。
特点:
- 用例维护量大;
- 页面相关性强,必须后期项目页面开发完后介入;
- UI测试适合与界面变动较小的项目;
在这里主要以Web UI自动化测试框架Selenium进行学习。
1.3 如何实施自动化测试
自动化测试的具体实现,应该包含以下七个过程
- 分析:总体把握系统逻辑,分析出系统的核心体系框架;
- 设计:设计测试用例,测试用例要足够明确和清晰,覆盖面广而精;
- 实现:实现脚本,有两个要求,断言和合理的运用参数化;
- 执行:执行中的异常需要我们仔细分析原因;
- 总结:对测试结果进行分析,对测试过程进行总结;
- 维护:难以解决但是必须解决;
- 分析:在自动化测试过程中深刻分析自动化测试用例的覆盖风险和脚本维护成本。
2. Selenium和Junit
2.1 Selenium
2.1.1 是什么
**web应用中基于UI的自动化测试框架,**支持多平台,多浏览器,多语言。
早期的Selenium RC已经被现在的webDriver所替代,可以理解为 selenium2.0 = selenium1.0 +webDriver现在提到selenium一般指的是2.0,组成部分:Selenium IDE,webDriver,selenium Grid;Selenium IDE :用于Selenium测试的集成开发环境,可以直接录制在浏览器的用户操作,并且可以回放、编辑和调试测试脚本。调试过程中可以逐步进行调整执行的速度,并且可以在底部浏览日志出错信息。webDriver:Selenium RC在浏览器中运行JavaScript 应用,会存在环境沙箱问题,而webDriver可以跳出JavaScript的沙箱,针对不同的浏览器创建更健壮的、分布式的、跨平台自动化测试脚本,基于特定语言绑定来驱动浏览器对Web元素进行操作和验证。
- 沙箱模式(Sandbox Pattern),顾名思义沙箱模式是创建了一个"沙箱",可以理解为创建了一个黑盒,我们不管在里面做什么都不会影响到外面。而在JavaScript中就意味着,在沙箱中的操作被限死在当前作用域,不会对其他模块和个人沙箱造成任何影响。
webDriver工作原理
- 启动浏览器后,Selenium - webDriver 会将目标浏览器绑定到特定的端口,启动后的浏览器则作为webDriver的远程服务器【remote server】
- 客户端(测试脚本)借助ComandExecutor发送HTTP请求给server端【通信协议:The webDriver Wire Protocol,在HTTP request 的body中,会以webDriver Wire协议规定JSON格式的字符串来告诉Selenium我们希望浏览器接下来的操作】
- Server端依赖原生的浏览器组件,转换为Web Serive的命令为浏览器本地的【native】的调用来完成操作
Selenium Grid:是一个服务器,提供对浏览器实例访问的服务器列表,管理各个节点的注册和状态信息,可以实现在同一时刻不同服务器上执行不同的测试脚本。
2.1.2 特点
- 免费,不需要破解
- 小巧,只是一个包
- 支持的语言多
- 支持多平台,多浏览器
- 支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器执行,相当于分发机的功能
2.1.3 工作原理
2.2 Selenium + Java环境搭建
Java最低要求为8,浏览器推荐使用chrome;
2.2.1 下载Chrome浏览器
2.2.2 查看Chrome版本
2.2.3 下载浏览器驱动
找到对应的驱动,并且要确保版本与Chrome的版本一致。
2.2.4 配置环境变量PATH
解压下载好的驱动压缩包,将下载好的chromedriver.exe放到Java的bin目录下;
2.2.5 验证环境搭建是否成功
(1)创建Java项目,添加pom依赖
java
<dependencies>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
</dependencies>
注意:版本选择selenium3
(2)编写运行代码
java
public class Main {
public static void main(String[] args) {
//创建驱动对象之前手动指定chromedriver.exe所在路径:
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
webDriver.get("https://www.baidu.com");
//WebElement element = webDriver.findElement(By.cssSelector(".s_ipt"));
WebElement element1 = webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));
element1.sendKeys("首次测试");
}
}
(3)点击运行
安装成功啦~~~
如果报错:ConnectionFailedException/403,需要加上代码
javaChromeOptions options = new ChromeOptions(); options.addArguments("--remote-allow-origins=*"); WebDriver driver = new ChromeDriver(options);
接下来的介绍需要重点掌握Selenium常用API的使用、操作测试对象以及添加等待打印信息、键盘事件、鼠标事件等案例的掌握
2.3 Selenium实操
2.3.1 元素定位
(1)css选择器
找到页面中的百度输入框
找到并返回类型
java
WebElement element = webDriver.findElement(By.cssSelector(".s_ipt"));
java
//输入软件测试
element.sendKeys("首次测试");
CSS选择器语法:
- id选择器: #id
- 类选择器:.class
- 标签选择器:标签名
- 后代选择器:父级选择器 子级选择器
2.3.2 XPath定位
(1)绝对路径
不常用,效率低
(2)相对路径 [只介绍常用的]
-
相对路径 + 索引:[找输入框]
html//form/span[1]/input//form/span[1]/input
-
相对路径 + 属性值:找输入框和按钮
html//form/span[2]/input
html
//input[@id = "su"]
-
相对路径 + 通配符
html//*[@* = "su"]
是基于属性值的简写
-
相对路径 + 文本匹配
html//a[text() = "新闻"]
2.3.3 操作测试对象
找到对应的百度一下输入框,输入"我爱编程",并点击按钮
(1)send_keys在对象上模拟按键输入
java
element1.sendKeys("我爱编程");
(2)click点击对象
java
webDriver.findElement(By.cssSelector("#su")).click();
(3)clear 清楚对象输入的文本内容
java
public static void test02() throws InterruptedException {
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
webDriver.get("https://www.baidu.com");
sleep(3000);
//打开百度页面,找到对应的输入框,输入文字
webDriver.findElement(By.cssSelector("#kw")).sendKeys("今天你学习了么");
//找到按钮点击
webDriver.findElement(By.cssSelector("#su")).click();
sleep(5000);
//清空输入框中的数据
webDriver.findElement(By.cssSelector("#kw")).clear();
}
(4)submit 提交
如果点击的元素放在form标签中,此时使用submit效果 = click,非form表单中无效。
(5)text 用于获取元素的文本信息
输入"我爱编程",并点击按钮,如果1返回的结果中包含"我爱编程"字眼,则测试通过,否则测试不通过。
java
//校验
//找到搜索的结果
int flag = 0;
List<WebElement> elements = webDriver.findElements(By.cssSelector("a em"));
for (int i = 0; i < elements.size(); i++) {
//如果返回的结果有"我爱编程",证明测试通过,否则不
if(elements.get(i).getText().equals("我爱编程")){
flag = 1;
System.out.println("测试通过");
break;
}
}
if(flag == 0){
System.out.println("测试不通过");
}
运行代码返回结果:
2.3.4 添加等待
(1)sleep休眠
添加休眠非常简单,引入time包,就可以在脚本中自由添加休眠时间了,这里的休眠指的是固定休眠。
(2)隐式等待
如果等待时间是3天,sleep就是三天,隐式等待最长等待3天,如果在3天内获取到页面上的元素,此时就执行下面的代码,如果三天还是没有找到,此时就报错。
java
webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.DAYS);
扫描当前页面,不能针对某一确定元素等待,等待所有的元素。
(3)显式等待
等待后面的条件,是程序员自己设置的。
2.3.5 打印信息
打印title + 打印url
java
public static void test03(){
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
webDriver.get("https://www.baidu.com");
//获取链接
String url = webDriver.getCurrentUrl();
//获取标题
String title = webDriver.getTitle();
if(url.equals("https://www.baidu.com/") && title.equals("百度一下,你就知道")){
System.out.println("当前页面url:" + url + ",当前title:" +title );
System.out.println("测试通过");
}else {
System.out.println("测试不通过");
}
}
2.3.6 浏览器操作
浏览器最大化 浏览器前进 后退 浏览器滚动条
java
public static void test04() throws InterruptedException {
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
//打开百度首页
webDriver.get("https://www.baidu.com");
//搜索二叉树
webDriver.findElement(By.cssSelector("#kw")).sendKeys("二叉树");
webDriver.findElement(By.cssSelector("#su")).click();
//强制等待3秒
sleep(3000);
//浏览器后退
webDriver.navigate().back();
//强制等3秒
sleep(3000);
webDriver.navigate().refresh();
//浏览器前进
webDriver.navigate().forward();
//强制等待3秒
sleep(3000);
//将浏览器滚动条滑倒最底端
((JavascriptExecutor)webDriver).executeScript("document.documentElement.scrollTop=10000");
//将浏览器最大化
webDriver.manage().window().maximize();
sleep(3000);
webDriver.manage().window().fullscreen();
sleep(3000);
//设置浏览器的长宽
webDriver.manage().window().setSize(new Dimension(700,800));
}
2.3.7 键盘事件
java
sendKeys(Keys.CONTROL,"A/C/X/V")
java
public static void test05() throws InterruptedException {
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
//打开百度首页
webDriver.get("https://www.baidu.com");
//搜索新闻联播
webDriver.findElement(By.cssSelector("#kw")).sendKeys("新闻联播");
webDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"A");
sleep(3000);
webDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"X");
sleep(3000);
webDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"V");
sleep(3000);
}
2.3.8 鼠标事件
java
public static void test06() throws InterruptedException {
System.setProperty("webdriver.chrome.driver","C:\\Program Files\\Java\\jdk1.8.0_192\\bin\\chromedriver.exe");
WebDriver webDriver = new ChromeDriver();
webDriver.get("https://www.baidu.com");
webDriver.findElement(By.cssSelector("#kw")).sendKeys("新闻");
webDriver.findElement(By.cssSelector("#su")).click();
sleep(3000);
//找到图片按钮
WebElement webElement = webDriver.findElement(By.cssSelector("#s_tab > div > a.s-tab-item.s-tab-item_1CwH-.s-tab-pic_p4Uej.s-tab-pic"));
//鼠标右击
Actions actions = new Actions(webDriver);
sleep(3000);
actions.moveToElement(webElement).contextClick().perform();
}
接下来学习针对特殊的场景进行测试。
2.3.9 定位一组元素
java
List<WebElement> webelements = findElements(By.cssSelector("input"));
for(int i = 0; i < webElements.size(); i++) {
// 如果每个元素type值等于checkbox进行点击
// getAttribute获取页面上的元素属性值,里面的type是当前元素属性
if(webElements.get(i).getAttribute("type").equals("checkbox")){
webElements.get(i).click();
} else {
// 否则什么也不操作
;
}
}
2.3.10 多层框架 / 窗口定位
javaswitchTo().frame("name/id/frame_element")
通过frame的id或者name或者或者frame自带的其它属性来定位框架,这里的switchTo().frame()把当前定位的主体切换到frame里。
javaswitchTo().default_content
从frame中嵌入的页面跳转出来,跳回到最外面的默认页面中。
java
webDriver.switchTo().frame("f1");
webDriver.findElement(By.cssSelector("body > div > div > a")).click();
2.3.11 下拉框处理
java
WebElement webElement = webDriver.findElement(By.cssSelector("#ShippingMethod"));
Select select = new Select(webElement);
select.selectByValue("15");
2.3.12 alert
- text 返回alert/confirm/prompt 中的文字信息
- accept 点击确认按钮
- dismiss 点击取消按钮
- sendKeys输入值,如果alert没有对话框的话就不能用,否则报错
java
webDriver.findElement(By.cssSelector("button")).click();
sleep(3000);
// alert弹窗取消
webDriver.switchTo().alert().dismiss();
sleep(3000);
// 点击按钮
webDriver.findElement(By.cssSelector("button")).click();
// 在alert弹窗中输入好好学习
webDriver.switchTo().alert().sendKeys("好好学习");
// alert弹窗确认
sleep(3000);
webDriver.switchTo().alert().accept();
2.3.13 上传文件
java
webDriver.findElement(By.cssSelector("input")).sendKeys("D:\\untitled");
2.3.14 补充
(1)关闭浏览器
关闭当前页面(原始页面),不会清空缓存
java
webDriver.close();
关闭了整个浏览器,会清除缓存
java
webDriver.quit();
(2)切换窗口
java
// 通过getWindowHandles获取所有的窗口句柄
// 通过getWindowHandle获取的get打开的页面窗口句柄
System.out.println(webDriver.getWindowHandle());
Set<String> handles = webDriver.getWindowHandles();
String target_handle = "";
for(String handle:handles) {
target_handle = handle;
}
webDriver.switchTo().window(target_handle);
sleep(3000);
webDriver.findElement(By.cssSelector("#ww")).sendKeys("新闻联播");
(3)截图
java
webDriver.findElement(By.cssSelector("#kw")).sendKeys("软件测试");
webDriver.findElement(By.cssSelector("#su")).click();
sleep(3000);
File file = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(file, new File("D://jietu.png"));
2.4 Junit
首先在pom.xml添加Junit依赖
java
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version>
</dependency>
这里使用的是Junit5,Junit是针对Java的单元测试框架。
2.4.1 注解
|-------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 注解 | 含义 | 代码示例 |
| @Test | 表示当前的方法是一个测试用例 添加依赖 pom.xml(Junit 5 ) | java @Test void Test01() { System.out.println("这是JunitTest里面的Test01"); }
|
| @Disabled | 忽略 跳过 | java @Disabled void Test03() { WebDriver webDriver = new ChromeDriver(); webDriver.get("https://www.baidu.com"); webDriver.findElement(By.cssSelector("#s-top-left > a:nth-child(6)")); }
|
| @BeforeAll | 所有测试用例执行前跑的代码 | java @BeforeAll static void SetUp() { System.out.println("BeforeAll里面的语句"); }
|
| @AfterAll | 所有测试用例执行后跑的代码 初始化在前(创建驱动、打开网页) | java @AfterAll static void TearDown() { System.out.println("这是AfterAll的语句"); }
|
| @AfterEach | 每一次测试用例执行之后 关闭资源在后(关闭浏览器) | java @AfterEach void AfterEachTest() { System.out.println("这是AfterEach里面的语句"); }
|
| @BeforeEach | 每一次测试用例执行之前 | java @BeforeEach void BeforeEachTest() { System.out.println("这是BeforeEach里面的语句"); }
|
参数 | 注解 | 代码示例 |
---|---|---|
单参数 | @ParameterizedTest | java void Test04(int num) { System.out.println(num); } |
单参数 | @ValueSource(ints = {1, 2, 3}) | java void Test04(int num) { System.out.println(num); } |
CSV获取参数 | @ParameterizedTest | java void Test06(String name) { System.out.println(name); } |
CSV获取参数 | @CsvFileSource(resources = "test01.csv") | java void Test06(String name) { System.out.println(name); } |
方法获取参数 | @ParameterizedTest | java public static Stream<Arguments> Generator() { return Stream.of(Arguments.arguments( "1,张三", "2,李四" )); } @ParameterizedTest @MethodSource("Generator") void Test04(String num, String name) { System.out.println(num + name); } |
方法获取参数 | @MethodSource("Generator") | java public static Stream<Arguments> Generator() { return Stream.of(Arguments.arguments( "1,张三", "2,李四" )); } @ParameterizedTest @MethodSource("Generator") void Test04(String num, String name) { System.out.println(num + name); } |
多参数 | @ParameterizedTest | java @ParameterizedTest @CsvSource({"1, 2, 3, ''"}) void Test02(String x, String y, int z, String q) { System.out.println(x); System.out.println(y); System.out.println(z); System.out.println(q); System.out.println("============================"); } @ParameterizedTest @CsvFileSource(resources = "test02.csv") void Test03(int num, String name) { System.out.println("学号:" + num + ",姓名:" + name); } |
多参数 | @CsvSource | java @ParameterizedTest @CsvSource({"1, 2, 3, ''"}) void Test02(String x, String y, int z, String q) { System.out.println(x); System.out.println(y); System.out.println(z); System.out.println(q); System.out.println("============================"); } @ParameterizedTest @CsvFileSource(resources = "test02.csv") void Test03(int num, String name) { System.out.println("学号:" + num + ",姓名:" + name); } |
[#### 2.4.2 参数化] |
|-----|------------------------------------------------------------------------------------------------------------------------------------------|
| 相等 | java @ParameterizedTest @ValueSource(ints = {1}) void Test01(int num) { System.out.println(num); Assertions.assertEquals(1, num);
|
| 不相等 | java Assertions.assertNotEquals(1,num)
|
| 为空 | java Assertions.assertNull(str)
|
| 不为空 | java Assertions.assertNotNull(str)
|
[#### 2.4.3 断言 assert]
2.4.4 测试套件 suite
引用依赖
java
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
java
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
@Suite
(1)通过class运行测试用例 (2)通过package
java
@Suite
// 通过class测试用例运行
@SelectPackages(value = {"Test09", "Test08"})
//@SelectClasses({JunitTest03.class, JunitTest.class, JunitTest01.class})
public class RunSuite {
}
下篇预告:自动化测试实战 -- "西西简易博客系统"的自动化测试