自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。
常见的自动化测试工具:QTP、selenium、Rational Robot 、jmeter、appium、soapui、Loadrunner等等
selenium 是一个web 的自动化测试工具,不少学习功能自动化的同学开始首选selenium , 相因为它相比QTP 有诸多有点:
selenium IDE,可以安装到浏览器的插件上,Microsoft Edge浏览器中,可以不用,就能安装IDE,而像Chrome浏览器,好像要翻出去才能安装。这里就不细说Chrome怎么装了,以下是Edge浏览器的安装流程。
安装成功后,在“地址栏”的右边就会出现图标,又或者点击扩展就能看到。
录制脚本过程如下:
打开IDE后,点击第一行蓝色字体;
输入工程名
输入被测试的网址
点击 “START RECORDING”后,会打开浏览器,并且进入到被测试的网站里
进入测试操作,比如输入“彭于晏”。然后鼠标点击IDE的停止按钮,脚本就录制好了。
输入这次测试的名称
运行录制的脚本
导出录制的脚本
以上就是用selenium IDE,进行录制脚本,并且将录制的脚本转换为编程语言,例如Python。
我自己安装的Python3.10版本,编译环境用的是pycharm。
在安装Python解释器的时候,直接用默认安装路径即可,如下,是我的安装路径:
pycharm的配置:
上诉是在pycharm上安装selenium包,有的人可能安装不上,那么可以试试在cmd命令行窗口试试这条命令:
pip install selenium // 用于安装selenium
pip show selenium //用于查看是否已经安装selenium
安装好后,就是这样的
selenium安装好之后,就只差一个浏览器驱动了。不同的浏览器,不同的版本,驱动是不一样的。Chrome浏览器,国内有镜像源
可以在这里找到Chrome的驱动。Edge浏览器,好像不用也能够下载到。
在下载驱动的时候,驱动的版本号要和浏览器的版本号对应上,有可能你安装的浏览器的版本号,找不到相应的驱动。安装临近的驱动版本号也是可以的。如图:
将下载好的驱动,解压到Python解释器的安装路径下(Script文件夹),如下:
至此,环境就搭建好了。
在讲解API时,我们首先要知道selenium需要导包的,如下:
from selenium import webdriver
# webdriver 就是浏览器的驱动,用这个驱动,能完成很多浏览器的操作
driver = webdriver.Edge() # 获取Edge浏览器的驱动
driver.get("https://www.baidu.com/") # 让浏览器打开百度搜索
在打开百度搜索之后,我们需要定位到“搜索框”,然后才能进行输入。那么如何定位到搜索框呢?我们有以下几种方法:
id 和name 是我们最最常用的定位方式,因为大多数控件都有这两个属性,而且在对控件的id 和name 命名时一般使其有意义也会取不同的名字。通过这两个属性使我们找一个页面上的属性变得相当容易。
在浏览器中,打开开发者工具(按F12)。就能查看到页面的每一个元素的前端代码。
# 在上文中,已经打开获得浏览器驱动的情况下,输入以下代码就能定位元素
driver.find_element_by_id("kw") # 通过id定位
driver.find_element_by_name("wd") # 通过name定位
看上图,知道搜索框的标签是“input”,并且class=“s_ipt”。那么我们也可以通过这两个属性进行定位。但是需要知道的是,使用tag name 进行定位,可能得到的不止一个标签的数据。所以在使用的时候,需要注意页面共有多少个相同的标签。
driver.find_element_by_tag_name("input") # 得到所有的input标签
driver.find_element_by_class_name("s_ipt") # 得到class="s_ipt"的元素
driver.find_element_by_css_selector("#kw") # 通过Css_selector定位
上图两个都行
driver.find_element_by_xpath("//*[@id="kw"]") # 通过xpath进行定位
有的时候,页面不是搜索框之类的,而是一条链接,也就是HTML中的a标签,此时就可以link text进行定位,值得注意的是,这种定位,输入的参数,也就是这条链接的名称,在当前页面只能是唯一的。只有这样,才能通过link进行定位。
driver.find_element_by_link_text("新闻").click() # 通过link text进行定位,并且点击
根据名称都知道,这就是link text的模糊查询。如下:
driver.find_element_by_partial_link_text("闻").click() # 通过partial_link text进行定位,并且点击
只通过一个“闻“字,就能够定位到”新闻“.
前面讲了很多的定位元素的操作,那么定位到元素之后,又有如下这些操作;
from selenium import webdriver
import time
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")
driver.find_element_by_id("kw").send_keys("彭于晏") # 搜索框 输入彭于晏
driver.find_element_by_id("su").click() # 点击 百度一下
time.sleep(2) # 停留2秒
driver.find_element_by_id("kw").clear() # 清空搜索框的内容
driver.find_element_by_id("kw").send_keys("胡歌") # 搜索框输入胡歌
driver.find_element_by_id("su").click() # 点击 百度一下
time.sleep(2)
driver.quit() # 关闭浏览器
以上就是一份完整的代码,先是搜索了彭于晏,然后清空输入框,再搜索胡歌。
在上面的代码中,可能你已经看到了time.sleep(),这就是等待。这个time,是需要导入time包的。除了这个,还有一个等待的函数
driver.implicitly_wait(5) # 智能等待
括号里面填的是最长等待时间,单位秒。
这个方法呢,可以在这样的场景下使用:
在搜索出结果后,需要点击百度百科,查看详细信息。因为在点击搜索彭于晏的时候,浏览器需要向后台发送请求,只能后台给出回应之后,才能看到这“百度百科”这几个字,这个时候才能点击。所以在点击百度百科的时候,是需要等待后台响应的,这段时间,人们是不能把控的,就可以通过智能等待,这个函数。
上诉代码的意思就是,在5秒之内,“百度百科”这几个字出来之后,就直接点击了。不需要傻傻的等待5秒。那么如果5秒后,还没点击的haul,就会报出异常。
from selenium import webdriver
import time
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")
driver.find_element_by_id("kw").send_keys("彭于晏") # 搜索框 输入彭于晏
driver.find_element_by_id("su").click() # 点击 百度一下
driver.implicitly_wait(5) # 智能等待
driver.find_element_by_link_text("百度百科").click() # 点击 百度百科,查看详情
time.sleep(2)
driver.quit()
可能通过一些函数,查看当前网页的URL和title的信息,如下
from selenium import webdriver
import time
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")
print(driver.title) # 打印title
print(driver.current_url) # 打印URL
driver.quit()
浏览器窗口的最大化和最小化:
driver.maximize_window() # 最大化
driver.minimize_window() # 最小化
除了最大化最小化,我们还可以手动的设置窗口的长宽。
driver.set_window_size(480, 800) # 长480,宽800
浏览器的后退和前进,也就是浏览器左上角的两个箭头。
driver.back() # 后退
driver.forward() # 前进
控制网页的滚动条。python是做不到这个操作的,只能通过javaScript的语句,来达到。
js = "var q=document.documentElement.scrollTop=10000" # 这里设置为10000,表示滚动条拉到最下面
driver.execute_script(js) # 执行上面的JavaScript语句
scrollTop 设置为10000,表示滚动条拉到最下面。相反的,设置为0,就表示将滚动条拉到最上面。execute_script(script, *args),在当前窗口/框架同步执行javaScript 。
在有些场景,我们是需要使用键盘的,我们也是可以通过驱动,来按动键盘,也可以使用组合键,比如Ctrl + C等等。
# 首先是需要进行导包的
from selenium.webdriver.common.keys import Keys
# 通过send_Keys(),来按键
send_Keys(Keys.TAB) # tab键
send_Keys(Keys.ENTER) # 回车键
# 组合键的用法
send_Keys(Keys.CONTROL, 'a') # Ctrl + a
send_Keys(Keys.CONTROL, 'c') # Ctrl + c
# 举个例子
# 搜索框先搜索彭于晏,然后Ctrl+x,剪切彭于晏。再重新输入胡歌进行搜索
# 键盘事件
from selenium import webdriver
from selenium.webdriver.common.keys import Keys # 需要导入keys包
import time
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")
driver.find_element_by_id("kw").send_keys("彭于晏")
driver.find_element_by_id("su").submit()
time.sleep(2)
# 键盘的组合键
# 将上诉输入的内容进行剪切,然后重新输入新的内容
# 首先还是需要先定位到元素
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x')
time.sleep(3)
# 重新输入新的数据
driver.find_element_by_id("kw").send_keys("胡歌")
driver.find_element_by_id("kw").send_keys(Keys.ENTER)
time.sleep(3)
driver.quit()
还是一样的,需要使用到鼠标,首先还是需要先进行导包
from selenium.webdrive.common.action_chains import ActionChains
这个类里面有以下几种操作:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")
driver.find_element_by_id("kw").send_keys("胡歌")
p = driver.find_element_by_id("su") # 定位到 百度一下按钮
ActionChains(driver).double_click(p).perform() # 双击百度一下按钮
time.sleep(6)
driver.quit()
ActionChains(driver), 生成用户行为。所有的行为都存储在ActionChains对象里面,通过perform()存储的行为。
double_click§,双击p所指向的元素。
perform(),执行所有存储的行为。
多层框架或窗口的定位:
对于一个现代的web 应用,经常会出现框架(frame) 或窗口(window)的应用,这也就给我们的定位带来了一个难题。有时候我们定位一个元素,定位器没有问题,但一直定位不了,这时候就要检查这个元素是否在一个frame 中,seelnium webdriver 提供了一个switch_to_frame 方法,可以很轻松的来解决这个问题。switch_to_frame(name_or_id_or_frame_element):可以简单记忆一下,如果这个frame有name和id属性那么就用这两个属性就好,如果没有的话可以先用find_element_by_xxx方法找到这个frame元素,然后把这个元素传进去,这也是可行的。switch_to_frame()把当前定位的主体切换了frame里。怎么理解这句话呢?我们可以从frame的实质去理解。
frame中实际上是嵌入了另一个页面,而webdriver每次只能在一个页面识别,因此才需要用switch_to_frame方法去获取frame中嵌入的页面,对那个页面里的元素进行定位。
switch_to_default_content() :从frame中嵌入的页面中跳出,跳回到最外面的原始页面中。
还有可能嵌套的不是框架,而是窗口,还有针对窗口的方法:switch_to_window(),用法与switch_to_frame ()相同:
driver.switch_to_window(“windowName”)
上传文件也是比较常见的操作,比如在gitee上,重新上传自己账户的头像之类的,都是上传文件的操作。
其实思路还是跟上面介绍的操作一样的,首先还是需要先定位到上传按钮,然后直接send_Keys(),括号里面输入的就是图片的绝对地址。
# 首先定位到上传按钮
driver.find_element_by_link_text("上传").send_Keys(图片的路径)
在上文,我们在写代码时,都是一个测试用例,就要写一个.py
的源文件,而unittest框架,就能够使其简化,这个框架可以将一个测试用例,写成一个函数就行,我们只需要框架,这样就能一次性跑完所有的代码了。unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用。
作为单元测试的框架, unittest 也是可以对程序最小模块的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须需要知道所使用语言的单元测试框架。利用单元测试框架,创建一个类,该类继承unittest的TestCase,这样可以把每个case看成是一个最小的单元, 由测试容器组织起来,到时候直接执行,同时引入测试报告。
import unittest # 导包
class Test(unittest.TestCase):
# 写其他函数
像这样写,意思就是该类,继承与unittest的TestCase。然后在这个类里面写一个函数就行。那么该如何调用这个类呢?非常简单,如下:
# 调用函数
if __name__ == "__main__":
unittest.main()
在执行测试用例之前,我们还需要做一些准备工作
和收尾工作
,比如获取浏览器驱动、获取测试网址,又或者是关闭浏览器,这些操作,都是需要写在函数里面的。
完整的代码如下:
import unittest # 导包
from selenium import webdriver
class Test(unittest.TestCase):
def setUp(self): # 准备工作
self.driver = webdriver.Edge()# 获取浏览器驱动
self.url = "https://www.baidu.com/"
self.driver.maximise() # 浏览器最大化
def tearDown(self): # 收尾工作
self.driver.quit() # 关闭浏览器
if __name__ == "__main__":
unittest.main()
现在大致的样子,就是这样的。接下来就是需要我们写测试用例的代码了。
这个框架有个怪脾气,我们写的测试用例的函数名,必须是以“test_”开头,不然的话,他是不会执行这个函数的。如下
import unittest # 导包
from selenium import webdriver
class Test(unittest.TestCase):
def setUp(self): # 准备工作
self.driver = webdriver.Edge()# 获取浏览器驱动
self.url = "https://www.baidu.com/"
self.driver.maximise() # 浏览器最大化
def tearDown(self): # 收尾工作
self.driver.quit() # 关闭浏览器
# 测试:百度搜索 许嵩
def test_xusong(self):
driver = self.driver
driver.get(self.url)
driver.find_element_by_id("kw").send_keys("许嵩")
driver.find_element_by_id("su").click()
time.sleep(3)
if __name__ == "__main__":
unittest.main()
这样,我们就用unittest框架,跑了一个测试用例,如果还有其他测试用例,我们只需要在类里面另外在写一个函数即可。
执行流程:
假设有很多测试用例,都写进了测试类里面,在执行代码的时候,每执行一个测试用例,它就是重新调用setUP函数,这个测试用例执行结束之后,也会先调用tearDown函数,然后才执行下一个测试用例。
我们还可以使用类似于java中的注解,可以忽略某一个测试用例。
# 测试:百度搜索 许嵩
@unittest.skip("kipping") # 表示忽略这个函数,不会执行这个函数
def test_xusong(self):
driver = self.driver
driver.get(self.url)
driver.find_element_by_id("kw").send_keys("许嵩")
driver.find_element_by_id("su").click()
time.sleep(3)
除此之外,unittest框架还提供了断言,当测试用例很多时,程序猿眼睛可能是看不完的。这个时候我们就可以使用断言,用于判断实际结果与预期结果不相等时,就会提示测试失败。
# 测试:百度搜索 许嵩
def test_xusong(self):
driver = self.driver
driver.get(self.url)
driver.find_element_by_id("kw").send_keys("许嵩")
driver.find_element_by_id("su").click()
time.sleep(3)
title = driver.title # 获取当前网页的title
self.assertEqual(title, "许嵩_百度搜索", msg="与预期结果不一致")
time.sleep(2)
如上,当title不相等时,就会提示测试失败,在终端就能够看到这些信息。其他的一些断言方法:
当然,当断言失败,也就是预期结果与实际结果不相等时,我们还可以使用try、except语句,进行处理,将结果进行截图保存。
@data([u"彭于晏", u"彭于晏_百度搜索"], [u"胡歌", u"胡歌_百度搜索"],[u"理想", u"刘翔_百度搜索"])
@unpack # 对上诉数据进行打包,用于下面代码测试
def test_baidu2(self, first_value, second_value):
driver = self.driver
driver.get(self.url)
driver.find_element_by_id("kw").send_keys(first_value)
driver.find_element_by_id("su").click()
time.sleep(3)
title = driver.title
# self.assertEqual(second_value, title, msg="与描述不符合")
try:
self.assertEqual(second_value, title, msg="与描述不符合")
except:
self.get_screen(driver, first_value)
def get_screen(self, driver, file_name):
# 没有这个文件夹,就创建一个
if not os.path.exists("../htmlResult"):
os.makedirs("../htmlResult")
# 获取现在的时间
nowTime = time.strftime("%Y-%m-%d %H%M%S", time.localtime(time.time()))
# 获取截图--浏览器的截图
driver.get_screenshot_as_file('../htmlResult/' + nowTime + file_name + '.png')
time.sleep(1)
在所有的测试用例执行完之后,我们还可以自动生成一个测试报告,里面就写清楚了很多信息。我们需要通过HTMLTestRunner.py 来生成测试报告,这个python解释器没有自带,需要自己下载。
HTMLTestRunner下载
然后将下载的包,放到下面这个Lib文件夹里面。
# html报告生成
import os
import unittest
import time
import HTMLTestRunner
# 得到所有测试用例
import demo.demo9
def createSuite():
# 收集写好的.py的文件
suite = unittest.defaultTestLoader.loadTestsFromTestCase(demo.demo9.TestBaidu)
return suite
if __name__ == "__main__":
if not os.path.exists("../htmlResult"):
os.makedirs("../htmlResult")
now_time = time.strftime("%Y-%m-%d %H%M%S", time.localtime(time.time()))
file_name = "../htmlResult/" + now_time + ".html"
with open(file_name, 'wb') as fp:
# 生成HTMLRunner
runner = HTMLTestRunner.HTMLTestRunner(stream=fp,verbosity=2,title="测试报告",description="用例执行情况")
suite = createSuite()
runner.run(suite)
上诉代码中,使用到了suite。称为单元测试用例的集合,也就是将所有的.py
源文件全部集中起来,一起跑代码。有四种得到suite的方式:
在上文中的所有代码中,我们在输入参数的时候,都是事先在代码中写好的,不够方便。那么我们可以事先将所有的测试用例写到一个txt文件
中,然后我们调用一个函数,从文件中读取所有的测试用例。再使用数据驱动(ddt),将测试用例填入我们的代码中。
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成 。
# 命令行输入以下代码
pip install ddt # 安装ddt
pip show ddt # 查看ddt
导包:
import sys
import unittest, csv
import os
from selenium import webdriver
import time
from ddt import ddt, unpack, data, file_data # 新加的包
要想一个类,能够使用数据驱动,那么需要在这个类的前面写这一行代码:
要想一个函数,能够自动添加测试的数据,就需要在这个函数的前面写这样一行代码:(只需在括号里填数据就行)
或者还可以输入两个参数的形式:
上面这样的代码,虽然能够做到自动添加测试数据,但是还是不够好,如果测试数据有100万行,难道要全部一次性写在函数名前面吗?
显然没必要,此时就用到了txt文件。如下
我的txt文件内容如下,具体改怎么写读取文件的内容,还是要看你的txt文件是怎么洗的。(第一行要写data,表示这是测试数据)
也还可以读取json文件,这里我们就不多介绍了。
好啦,本期更新就到此结束啦,我们下期再见吧!!!