目录
1.什么是自动化以及为什么要进行自动化
2.为什么选择selenium作为web自动化工具
3.selenium环境部署
4.什么是驱动以及驱动的原理
5.selenium的基础语法和操作
5.1定位元素
5.2元素的操作
5.3等待
5.4信息打印
5.5窗口
5.6导航
5.7弹窗
5.8鼠标、键盘操作
5.9复选框和单选框
5.10下拉框
5.11上传文件
5.12屏幕截图
6.学习Junit
6.1注解
(1)@Test
(2)@BeforeEach和@BeforeAll
(3)@AfterEach和@AfterAll
6.2断言
(1)assertEquals(expect,actual)和assertNotEquals(expect,actual)
(2)assertTrue(expect和actual比较相等)和assertFalse(expect和actual比较不相等)
(3)assertNull()和assertNotNull()
6.3用例执行顺序
6.4测试套件(Suite)——一共两步操作
6.5参数化
一共需要两步完成
1.单参数:@ValueSource(类型={参数1,参数2})
2.多参数
3.动态参数
1.什么是自动化以及为什么要进行自动化
自动化测试能代替一部分的手工测试
自动化测试能够提高测试效率----->随着功能的增加、版本越来越多,版本回归的压力也就越来越大。所以仅通过人工测试来回归所有的版本是不现实的,因此需要借助自动化来进行回归。
2.为什么选择selenium作为web自动化工具
(1)开源免费(学生党友好)
(2)支持多浏览器(如Chrome、Firefox、IE、edge、Safari...)
(3)支持多系统(如Linux、Windows、MacOS)
(4)支持多语言(如java、python)
(5)selenium包底层有很多可使用的API
3.selenium环境部署
java版本最低要求8+Chrome浏览器+Chromedriver(谷歌浏览器驱动)+selenium工具包
具体操作看这篇: java+selenium环境搭建_在上山的mei的博客-CSDN博客
4.什么是驱动以及驱动的原理
为什么要说驱动的事,因为你看上面selenium环境搭建的时候是不是有一个谷歌浏览器驱动,因此我这里就来细说一下这个驱动他是干嘛的。
- 什么是驱动?
生活当中我们开车,汽车有驱动(两轮驱动、四轮驱动),它可以让汽车跑起来。
我们使用的计算机里也有驱动程序,它可以驱动计算机和设备工作起来。我们打开浏览器也需要驱动,平常我们手动的打开浏览器,这种就是人工手动的驱动打开浏览器;而如果我们不选择人工手动的驱动打开浏览器,而是使用自动化,由于代码不能直接打开浏览器,因此就需要借助驱动程序来协助打开浏览器。(这也就是为什么我们在进行自动化的时候需要浏览器驱动,而我这里选择的是在谷歌浏览器上操作,所以需要使用谷歌浏览器驱动)
- 驱动的原理——selenium、驱动、浏览器三者之间的关系
第一步:用selenium来编写自动化脚本代码,然后代码创建一个http请求并发送这个请求给webdriver浏览器驱动(webdriver浏览器驱动就是一个服务器)。
第二步:webdriver浏览器驱动接受这个请求,解析这个请求。第三步:webdriver浏览器驱动发送指令操控浏览器,浏览器执行测试步骤。
第四步:浏览器把结果返回给webdriver浏览器驱动,webdriver浏览器驱动再把结果返回给脚本。
上面四步就是一次自动化测试中selenium、驱动、浏览器三者之间的一个运作原理
用一句话总结上面自动化测试的原理就是:代码可以驱使驱动来打开浏览器
5.selenium的基础语法和操作
- 写在前面的话
需要知道的是,一次自动化测试的周期是这样的:
创建驱动对象打开浏览器---->噼里啪啦进行浏览器中的操作--->关闭浏览器
(下面写一个简单的例子用来说明最基本但是必要的这三部分代码格式)
5.1定位元素
怎么得知页面元素的这些信息呢?---使用开发者工具(检查)
打开浏览器--->检查--->点下图这个按钮,然后点页面中的想要了解的位置,就能在右侧这个检查中看到它对应的信息了(比如上面方法中的id、className、tagName...)
这里关于定位元素我就介绍两个比较常用的方法吧,一个是cssSelector,另一个是xpath
比如我想要定位百度一下这个网址中的输入框:
注意:元素的定位一定要唯一!!!!
怎么确定其唯一呢?----->右键检查,然后Ctrl+F会出现一个输入框
复制要检查的元素到输入框,如果显示 1 of 1,则表示属性在当前界面是唯一的,否则不是唯一的
5.2元素的操作
定位到元素之后,当然就要进一步对元素进行操作啦~
这里拓展说明一下:虽然像上面“不能通过getText()获取文本”的例子中的按钮里的“百度一下”不能通过getText()来获取,但是如果我们要获取到他,我们需要用到getAttribute("value")。原因是这个“百度一下”是value这个属性中的值。
5.3等待
- 为什么会有等待?
因为代码的执行速度比较快,而前端页面渲染的速度相对较慢一点,这就可能导致——代码已经执行到下一步了,页面还没有渲染出来,因此就找不到页面上本来有但是因为没有渲染出来而定位不到的元素。
- 等待分类
等待分为:强制等待、隐式等待、显示等待
(1)强制等待:让程序暂停一会儿,等待指定的时间之后继续执行下一步
优点:语法简单,适合调试的时候用
缺点:需要等待固定的时间,造成测试时间的大量消耗,大大降低了自动化的测试效率
关于这个缺点的说明我举一个例子你就知道了——1个测试用例使用强制等待,平均时间是3-5s,以5s为例,加入web自动化有100个,那么强制等待的时间就是100*5=500s=8min20s,这还是没加自动化执行的时间呢,如果加上可能就超过15min甚至更久!
(2)隐式等待:在规定的时间范围内,轮询等待元素出现之后就立即结束,如果在规定的时间内元素仍然没有出现,则会抛出一个NoSuchElementException异常
隐式等待特点:作用在webdriver整个生命周期,即只要没有走到driver.quit,隐式等待就一直存在。所以像上面那个例子,你把隐式等待放在哪里都可以,如下图所示。
优点:节省了大量的等待时间,元素展示之后就可以直接执行下一步,执行效率高
缺点:需要等待所有的元素都展现完才会执行下一步,仍然有额外的时间上的浪费
(3)显示等待:可以针对某一个元素进行测试
首先创建一个显示等待的对象(我这里举例为res),这个对象初始化的时候要传入两个参数,第一个是驱动对象(我这里为driver),第二个是强制等待的时间(我这里设为10),在这个时间之内会轮询的查找元素是否存在。等待什么时候为止呢?等待括号里的条件满足为止,如果条件在指定时间内没有满足,就抛出异常。括号里的内容:ExpectedConditions类中的presenceOfElementLocated方法(查看页面是否存在对应的元素),通过定位元素来查看当前要查找的元素是都在页面存在,如果存在就结束.如果不存在就会报异常。
下面这个例子我是针对页面中这一元素进行的等待:
或者用ExpectedConditions类中的textToBe方法(检查元素对应的文本是否符合预期)也可以
注意哈,你有可能照着我这个例子打,然后运行之后发现报错,莫慌莫慌,一般你再重新运行一两遍就好了。原因就是我选的这个元素啊它有时候会小小的变一下他的cssSelector,比如从#32变到#31,所以你代码里cssSelector复制的是#32的,但是可能某次运行的时候,这个元素它变成了在页面上的#31位置,所以找不到是正常滴。之所以说让你再运行一遍,是因为下次这个元素可能就又回到#32位置了哈哈哈哈,你懂我意思么~
对于这个格式中的内容再补充一点解释:
1. ExpectedConditions 是selenium里的一个类,类里提供了很多方法用来测试。presenceOfElementLocated就是这个类中的一个方法,用来查看页面是否存在对应的元素。
textToBe方法也是这个类中的方法,用来检查元素对应的文本是否符合预期,第一个参数是实际上的,第二个参数是期待的
优点:针对某一个元素进行等待,极大降低了自动化整体的等待时间
缺点:写法更为复杂
- 代码里面可以同时使用显性等待和隐式等待吗?
不建议同时使用,因为可能会出现一些意想不到的效果。
下面举个例子:假如我让隐式等待5s,显示等待10s,最终结果显示它等待的时间经过两次运行验证为11s。
5.4信息打印
(1)打印当前页标题(title)
(2)打印当前页Url
(3)打印点击超链接后打开的新的页面的title和url
举一个例子:当前是百度首页,点击首页上的“新闻”超链接,此时打开了一个新的页面(新闻超链接对应的页面)
当我按照上面(1)和(2)的方式来打印title和url的时候,发现出现问题了:title实际结果和预期结果不一样,url实际结果和预期结果不一样
可是检查了下,自己的代码写的没问题啊。那问题出在哪儿了?
其实问题出在对于selenium来说,他并不知道点击“新闻”链接后应该展示的页面是什么。换句话说,我们手动操作的时候,点击“新闻”超链接,打开了一个新的页面(新闻超链接对应的页面),我们来到这个新页面能够看到他的title和url。但是,selenium不知道。所以即便使用selenium操作的时候,也是成功的打开了新页面的,但是selenium由于没有咱们人的判断力,因此他还是停留在原来的页面,所以这个时候打印title和url自然就还是原来页面的了。
上面的问题源头找到了,那怎么解决他呢?
------------答案就是:通过获取页面句柄,从而进行页面(窗口)切换。
什么是句柄呢?selenium对每一个标签页都有唯一标识,这个唯一标识就叫句柄
如何通过获取页面句柄来实现页面(窗口)切换呢,请继续往下看5.5窗口中的内容。
5.5窗口
(1)获取当前页面的句柄 (2)获取所有页面的句柄
下面这个是举例:当前是百度首页,获取当前页面句柄。然后点击百度首页上的“新闻”超链接,此时打开了一个新的页面(新闻超链接对应的页面),获取当前页面句柄(当然根据我们上面所说,你应该知道这里为什么句柄还是百度首页的句柄),最后获取目前所有页面的句柄。
解释一下:
(3)窗口切换
有了上面(1)和(2)的铺垫,下面我们来解决一下5.4中(3)里未解决的问题!!
解释一下:
(4)窗口大小的设置
- 窗口最大化
- 窗口最小化
- 设置指定的窗口大小
- selenium不能直接编译js语言,但是可以使用driver.executeScript()方法来执行js语言
通过这个方法可以调整窗口滚动条的位置
5.6导航
selenium中提供了navigate接口来实现页面的导航
- 前进
- 后退
正好用到这个网址做例子,顺带插一句,推荐这个网站:在线工具 - 你的工具箱
- 一个小实践练习:利用上面学的窗口和导航的知识,我们可以实践一下——打开一个页面,点击链接跳转到新的页面,再执行前进和后退操作,实际结果是什么呢?
根据我上面的代码可以发现,实际结果是不论导航后退还是导航前进,都一直停留在新打开的页面
5.7弹窗
(1)普通弹窗:普通弹窗区别于下面要讲的几个弹窗,它可以在前端页面直接使用driver.findElement()定位到元素
举个例子:
(2)不普通的弹窗
以下三种弹框不能够在前端定位到元素,需要使用selenium中提供的Alert接口来处理
,切换到弹窗上格式:Alert arlert=drive.switchTo().alert();
- 警告弹窗
- 确认弹窗
- 提示弹窗
5.8鼠标、键盘操作
(1)鼠标操作
selenium提供了Actions包,鼠标操作会用到
下面演示的是鼠标的其中一种操作——鼠标的点击和等待
(2)键盘操作
5.9复选框和单选框
5.9中的内容后续我会补上,大家要是等不了的话,上网查也是能查到的~
5.10下拉框
selenium提供了select包
(1)方法1:通过索引来定位选项,索引从0开始计数
(2)方法2:通过value属性来选择
(3)方法3:通过可见的文本来选择
我把这三个方法写在一个例子里了
5.11上传文件
页面上点击文件上传会弹出系统窗口,selenium不能操作系统窗口,因此通过在sendKey()方法中输入“想要上传的文件路径以及文件名”,就能实现文件上传
5.12屏幕截图
屏幕截屏的目的:如果运行结果出错了,可以通过屏幕截图看到是哪里出错。就像生活中高速公路拍车辆违章的摄像头,会有一个照片用来留证。
因为屏幕截图会用到FileUtils包,因此需要先添加依赖:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
屏幕截屏语法格式:
driver.getScreenshotAs(OutputType.x);其中x可以选择File、Base64、bytes,我这里选择了File
根据上面这个例子运行,运行结果是出错了,我们看看屏幕截图成功没有
发现刚刚指定的屏幕截图文件路径下有了屏幕截图,如上图所示
6.学习Junit
- Junit是个啥?
Junit是一个开源的java语言的单元测试框架,他是java方向使用最广泛的单元测试框架
- 为什么要学习Junit?
Junit一般都是用于单元测试,但是!这里用到Junit不是为了单元测试,而是利用Junit配合selenium完成自动化测试。前面我们学习selenium的基础语法和操作的时候已经学习到了怎么写一些典型的自动化代码,但是自动化代码和自动化测试是有区别的,区别在于自动化代码只是一个操作,要想达到测试的效果,我们需要知道测试的结果成功与否,因此就需要Junit的帮助。
所以你现在知道了:web自动化测试=selenium基础语法+Junit基础
- 添加Junit相关依赖(别忘加完点更新)
<dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-suite</artifactId> <version>1.8.2</version> <scope>test</scope> </dependency> </dependencies>
注意:我这里添加的是Junit5的依赖,他需要java版本最低要求为8
6.1注解
(1)@Test
被@Test注解的方法被视为一个用例,那么我们就可以在不创建一个专门的运行自动化代码方法的类的情况下,在包含被@Test注解的方法的类中直接右键运行了
这里唤醒一下你的记忆:前面学习selenium基础语法的时候,我是创建了两个类的,其中专门有一个类是用来运行另一个写自动化代码的类中的方法的
那么有了这个@Test注解后,我们就可以不创建这样专门的运行入口的类啦
下面来看看举例就明白了:
先不加@Test注解看看
加上@Test注解
运行看看
找不同
你会发现,我们当前这种方式运行方法的运行结果不但会告诉你是正常退出还是非正常退出,以及如果非正常退出的报错原因(即左图运行结果),还能看到具体是哪个方法报的错,如果通过就会打对勾,没通过就会打叉。这样看来,使用@Test注解的方式更能帮助我们发现问题解决问题。
(2)@BeforeEach和@BeforeAll
这两个一块说是因为,他俩都表示被注解的方法应该在其他方法执行前被执行
下面具体各自介绍这两个注解:
@BeforeEach:被它注解的方法要在其他每个方法执行前都执行一遍
看了上图的举例演示,你可能有一个疑问:刚刚不是说要加@Test注解才能在本类中运行方法吗,那上面图中的aaa()方法为什么在没加@Test注解的情况下也执行了呢?
----答案是:@BeforeEach注解也可以让被注释的方法被执行。但是注意他俩的区别是,@Test注解修饰的方法就是作为方法执行,你在运行结果的左栏(通过打钩,不通过打叉那里)可以看到aaa()方法的执行结果;而@BeforeEach注解的方法更像是让被注解的方法作为一种标记(我愿意这么理解),因此在运行结果的左栏看不见aaa()方法。还有啊还有,既然说到这里了,那我也就都提前说下,除了此处讲的@BeforeEach注解,下面讲的其他注解也都和@BeforeEach注解一样的特点
那么你可能还有这样一个问题:那么@Test注解和@BeforeEach注解可以同时注解一个方法吗,效果是什么样的?
----答案是:可以,效果如下图(注意:图中的注解二字不小心都写成了注释,大家看到的时候知道我写错了就行了,应该是注解注解注解!)
说完@BeforeEach注解,下面说@BeforeAll注解
@BeforeAll:被它注解的方法在其他所有方法执行前只需执行一遍
注意:使用@BeforeAll注解的方法必须定义为static
@Test注解和@@BeforeAll注解同时注解的效果:
(3)@AfterEach和@AfterAll
这两个一块说是因为,他俩都表示被注解的方法应该在其他方法执行后被执行
下面具体各自介绍这两个注解:
@AfterEach:被它注解的方法要在其他每个方法执行后都执行一遍
@Test注解和@@BeforeAll注解同时注解的效果:
说完@AfterEach注解,下面来说@AfterAll
@AfterAll:被它注解的方法在其他所有方法执行后只需执行一遍
注意:使用@AfterAll注解的方法必须定义为static
@Test注解和@BeforeAll注解同时注解的效果:
6.2断言
- 为什么要用断言?
在没使用断言的时候,我们也能够得知测试结果,我们根据看是否是正常退出以及非正常退出时的抛出异常、以及如果使用的是@Test注解执行方法的时候,我们还能在左侧栏得知各个方法的执行结果,通过这些方面得知测试结果出错来源。但是方法中具体哪里出的错,我们不好判断。
而如果使用断言,我们不但能知道上述测试结果,还能根据断言的的结果直接找到问题所在。即如果断言结果是失败的话,就会在控制台有expect和actual的结果,通过这个结果你就知道具体问题出在哪里了。因此断言能帮我们更快更精准的在进行自动化测试的过程中找到问题。
Junit提供了Assertions包,在使用断言的时候会用到
(1)assertEquals(expect,actual)和assertNotEquals(expect,actual)
下面具体来说这两个断言:
assertEquals(expect,actual):校验期望值和实际值是否匹配,如果匹配则测试结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
assertNotEquals(expect,actual):校验期望值和实际值是否不匹配,如果不匹配则测试结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
(2)assertTrue(expect和actual比较相等)和assertFalse(expect和actual比较不相等)
下面具体来说这两个断言:
assertTrue(expect和actual比较相等):校验期望值和实际值比较相等,如果比较结果为相等相当于比较结果为True,那么断言结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
assertFalse(expect和actual比较不相等):校验期望值和实际值比较不相等,如果比较结果为不相等相当于比较结果为False,那么断言结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
(3)assertNull()和assertNotNull()
下面具体来说这两个断言:
assertNull():校验参数是否为null,若是则断言结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
assertNotNull():校验参数是否不为null,若是则断言结果为成功
看一个测试结果断言为成功的例子:
再看一个测试结果断言为失败的例子:
6.3用例执行顺序
- 为什么需要用到Junit里的排序方法?
为了应对这样的情况:如果用例之间存在关联关系,那么就需要手动的制定用例的执行顺序
Junit的默认执行顺序是不确定的,官方文档JUnit 5 User Guide中没有明确给出
但我们可以使用官方文档中提供的排序方法来手动设置用例的执行顺序(例如方法的排序、标签的排序......)
下面我将介绍用方法的排序这种排序方法来如何设置用例的执行顺序~
方法的排序——通过在方法上加@Order注解来排序方法,同时在被@Order注解的方法所在的类上面要加上@TestMethodOrder(MethodOrderer.OrderAnnotation.class)注解,表示当前类使用方法来进行排序
我们先来看看没有进行方法排序的时候的默认用例的执行顺序:
现在进行方法排序,再来看:
6.4测试套件(Suite)——一共两步操作
第一步首先先在作为测试套件的类上加上@Suite注解,用来表明这个类是一个测试套件
第二步接着有两种方式添加用例所在的类:
方法1:指定类,把需要执行的被@Test注解的方法所在的类的.class形式添加到套件中并执行
即在类上添加@SelectClasses({类1.class,类2.class})
举个例子:
如果添加到套件中的类当中没有一个方法是被@Test注解的,那么会怎样呢?
就比如下面这样:
那么我们能够看到,会出现No tests were found(中文就是找不到对应的用例)
方法2:指定包,添加到套件中并执行(执行包下面所有的文件中文件名字结尾带有Test的文件当中的所有被@Test注解的方法)
即@SelectPackages(包名)
6.5参数化
一共需要两步完成
第一步:使用@Parameterizedtest注解标注方法类型为参数化,被其标注的方法不需要再添加@Test注解,如果添加了,该用例会多执行一遍
第二步:选择如下参数分类中的一种进行参数化形式放到要被参数化的方法上
1.单参数:@ValueSource(类型={参数1,参数2})
需要注意的是:类型要使用下图这些类型的原生类型!!(像int的原生类型是ints,String的原生类型是strings)
举个例子:
2.多参数
2.1适合参数不那么多的情况使用
@CsvSource(value={"第一对的参数1,第一对的参数2","第二对的参数1,第二对的参数2"})
每对参数的分隔符默认是英文的逗号
每对参数的参数1都对应着multiParameter1()中的第一个参数的类型,每对参数的参数2都对应着multiParameter1()中的第二个参数的类型
问题1:上面说了,默认的分隔符是英文的逗号,那如果我想手动制定分隔符可以吗?
---答案:可以,那么@CsvSource中加一个对分隔符设置的参数就行了
举个例子:@CsvSource(value={"第一对的参数1->第一对的参数2","第二对的参数1->第二对的参数2"},delimiterString="->")
如下图我们举个例子看看:
问题2:如果参数中包含逗号怎么办?
---答案:需要使用英文单引号作为转义字符
举一个例子:
2.2适合参数很多的情况使用
参数多的时候,代码编写不美观,这个时候就可以把参数写在一个文件中,通过注入文件的方式来实现多参数参数化
@CsvFileSource(指定文件路径)
这个指定文件路径又可以分为两种指定法儿:
方法1:指定文件路径为当前项目下resources文件中的csv文件
方法2:指定文件路径为本地任意文件夹下的csv文件
注意:下面说的这个点适用于单参数和多参数的参数化(动态参数不用,因为你少一个会报错哈哈哈,你可以在下面学动态参数的时候自己试下就知道了)来看——对于非引用类型的参数数据必须有对应的值,否则会导致用例执行失败,因为如果你空着,执行结果会默认的给你空着的地方补一个null,但是null是引用类型的空值的意思,因此你要拿非引用类型就会类型不匹配就报错了
我下面举了多参数的两种指定路径的例子,大家看一下,理解记住就行:
3.动态参数
- 动态参数分类:单参数和多参数
(1)单参数
使用@MethodSource注解
@MethodSource注解具体下来又分为两种(用哪种都可以):一种是@MethodSource注解带参数,另一种是@MethodSource注解不带参数
先看@MethodSource注解带参数:
再来看看@MethodSource注解不带参数:
(2)多参数
使用@MethodSource注解
又分为两种(用哪种都可以):一种是@MethodSource注解带参数,另一种是@MethodSource注解不带参数
先看@MethodSource注解带参数:
再来看看@MethodSource注解不带参数: