本文主要以PO思想将页面元素和代码分离,以及执行多用例的UI自动化框架。
下图是整个框架图:
???????在准备测试一个功能时,我们可以通过手工去测,也可以用UI自动化框架去实现多用例的操作。
????????自动化框架思路:在拿到一个被测业务(功能)时,首先想一下,如果用手工的方式如何去测,需要准备哪些东西,需要哪些步骤,而自动化的过程就是代替人工的实现,所以我们往这个思路上去想不难得出,一个测试业务首先需要测试驱动(也就是浏览器),测试驱动上需要测试元素(也就是测哪些东西,再通俗一点就是所对应的界面或者功能),那如何用代码调用驱动进行实现,测试配置就在此起到了决定性的作用,将所有的配置好之后要进行测试,还需要测试数据和用例的准备,最后加入测试报告的配置用执行器执行就能够形成最终的测试报告了。这段文字主要是梳理思路以及解释上图。还有很多细节以及步骤且听我慢慢道来。
建包:
准备以下七个包:
在每个包内配置文件:
1.browserdriver(浏览器驱动)
在browserdriver中放置浏览器驱动
2.config(测试配置)
(1)创建config.ini文件里面配置要测试的网址和浏览器驱动
(2)创建read_ini.py:读取配置文件
源码如下:
#封装成别人可以使用的代码
import configparser
import os
print(os.path.dirname(os.path.dirname(__file__)))
class joReadIni():
#初始化 路径
# 参数1 file_name文件名指定文件名
# 参数2 node 指定所需要节点名
def __init__(self,file_name=None,node=None):
self.file_name=file_name
self.node=node
if file_name==None:
#文件名 加r 防止自动转义
self.file_name=os.path.dirname(os.path.dirname(__file__))+"/config/config.ini"
if node==None:
self.node = "jwconfig"
self.cf=self.load_ini(self.file_name)
#加载配置文件
def load_ini(self,file_name):
#获取解析配置对象
cf=configparser.ConfigParser()
cf.read(file_name, encoding='utf-8')
return cf
#获取配置文件中的内容===>指定key
def get_value(self,key):
return self.cf.get(self.node,key)
#进行单元测试
if __name__=='__main__':
aa=joReadIni()
print(aa.get_value("browser"))
(3)创建jo_driver.py:读取浏览器驱动产生测试webdriver驱动对象(调取浏览器)
源码如下:?
import os
from time import sleep
from selenium import webdriver
from config.read_ini import joReadIni
#工程路径
parent_path=os.path.dirname(os.path.dirname(__file__))
#专门读取配置文件对象
jo=joReadIni()
#获取被测试网址
url=jw.get_value("url")
def get_firefox_driver():
driver_path=parent_path+"/browserdriver/geckodriver"
driver=webdriver.Firefox(executable_path=driver_path)
driver.get(url)
driver.maximize_window()
driver.implicitly_wait(3)
sleep(3)
return driver
#进行单元测试
if __name__=='__main__':
get_firefox_driver()
3.business(测试业务)
利用PO的思想将页面元素和代码分离,因此先在business内创建ini元素文件,再到config包内创建一个py文件进行元素的查询,即使之后界面变了,但是元素是可以复用的,因此将其分离开来方便代码的管理。
(1)创建LocalElement.ini 文件:存放需要自动化操作的控件元素和定位方式
源码如下:
[jologin]
user_login=xpath>//*[@id="__layout"]/div/div[1]/div[1]/div[3]/div[3]/button[1]/span
user_name=xpath>//*[@id="__layout"]/div/div[1]/div[2]/div/div/div[3]/div/div/div[1]/div/input
user_password=xpath>//*[@id="__layout"]/div/div[1]/div[2]/div/div/div[3]/div/div/section/div/div/input
login_button=xpath>//*[@id="__layout"]/div/div[1]/div[2]/div/div/div[3]/div/div/div[2]/button
[joxuanke]
touxiang=xpath>//*[@id="__layout"]/div/div[1]/div[1]/div[3]/div[3]/div/img
xuexizhongxin=xpath>//*[@id="__layout"]/div/div[1]/div[1]/div[3]/div[3]/div/div/ul/li[1]/span
xuankezhongxin=link_text>选课中心
jiagepaixu=xpath>//*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/span[2]
#根据页面倒数第三段 div[2]决定了是第几个元素
secondkecheng=xpath>//*[@id="__layout"]/div/div[2]/div/div[2]/div[2]/div[2]/div[3]/button
kaishixuexi=xpath>//*[@id="__layout"]/div/div[2]/div[1]/div/div[3]/div/div[3]/button
# 看是否有评价决定是否成功
yanzheng=link_text>评价
(2) 再到config包内配置查找元素的py包(注意是config包内创建!)
创建jw_findelement.py 文件 (工具类:直接复制,不做修改即可!)封装了各种selenium查找元素的各种方式
源码如下:
import os
from time import sleep
from config.jo_driver import get_firefox_driver
from config.read_ini import joReadIni
class joFindElement():
def __init__(self,driver):
self.driver=driver
#获取元素
def get_element(self,file=None,node=None,key=None):
if file==None:
file=os.path.dirname(os.path.dirname(__file__))+"/business/LocalElement.ini"
if node==None:
node="jologin"
read_ini=joReadIni(file,node)
data=read_ini.get_value(key)
#得出定位方式 文件中是以 > 方式切割
by=data.split('>')[0]
value=data.split('>')[1]
try:
if by=='id':
return self.driver.find_element_by_id(value)
elif by=='name':
return self.driver.find_element_by_name(value)
elif by=='className':
return self.driver.find_element_by_class_name(value)
elif by=='link_text':
return self.driver.find_element_by_link_text(value)
elif by=='xpath':
return self.driver.find_element_by_xpath(value)
except:
return None
def gundongdaodibu(self):
self.driver.set_window_size(600, 600)
sleep(2)
# 通过JS设置浏览器窗口的滚动条位置
# 横向滚动100
# 纵向 滚动 450
js1 = "window.scrollTo(0, 600);"
self.driver.execute_script(js1)
if __name__=="__main__":
parent_path = os.path.dirname(os.path.dirname(__file__))
driver=get_firefox_driver()
aa=joFindElement(driver)
#测试是否可以在get_element 找到登录链接
print(aa.get_element(parent_path+"/business/LocalElement.ini","jologin","user_login"))
?一定要自测一下能不能找到该元素,能找到之后下一步就是测试功能是否能正常操作。(模拟手工测试)
(3)创建xuanke_bus.py:这个文件存放被测试的业务(核心模块)
源码如下:
from time import sleep
from selenium.webdriver import ActionChains
from config.jo_driver import get_firefox_driver
from config.jo_findelement import joFindElement
from config.read_ini import joReadIni
def jo_xuanke(username,password,kechenghao):
jo = joReadIni()
driver = get_firefox_driver()
# jw2 专门查找元素
jo2 = joFindElement(driver)
try:
# 登录
jo2.get_element(node="jologin",key="user_login").click()
jo2.get_element(node="jologin",key="user_name").send_keys(username)
jo2.get_element(node="jologin",key="user_password").send_keys(password)
jo2.get_element(node="jologin", key="login_button").click()
sleep(2)
#点击选课中心
jw2.get_element(node="joxuanke", key="xuankezhongxin").click()
#将浏览器滚动到底部------》然后开始点击排序
#jw2.gundongdaodibu()
# 按价格排序(点击两下--确保降序)---确保测试是免费课程
# //*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/span[2]
# 点击第一下
sleep(3)
jw2.get_element(node="joxuanke", key="jiagepaixu").click()
sleep(3)
# 点击第二下
jw2.get_element(node="joxuanke", key="jiagepaixu").click()
sleep(3)
# 这里的封装导致无法改变课程
#jw2.get_element(node="joxuanke", key="secondkecheng").click()
kecheng_xpath="//
[@id='__layout']/div/div[2]/div/div[2]/div[2]/div["+str(kechenghao)+"]/div[3]/button"
#选择的课程xpath
driver.find_element_by_xpath(kecheng_xpath).click()
sleep(2)
#点击开始学习()
jw2.get_element(node="joxuanke", key="kaishixuexi").click()
#验证是否有评价信息
sleep(2)
result = jw2.get_element(node="joxuanke",key="yanzheng")
sleep(2)
print("找回复是否有评价的结果是", result)
if result == None:
return False
else:
return True
except Exception as e:
print("JO",e)
print("流程异常,都认为选课失败")
# 发帖子过程出异常
return False
finally:
driver.quit()#没有这个测试过程中会打开三个浏览器
if __name__=="__main__":
jw_xuanke("19999999999","a123456",3)
接下来就是准备数据和用例开始执行了。
4.test_data(测试数据)
(1)创建xuanke.csv文件:用来存放测试数据
(2) 创建jotestdata.py 文件:读取准备的测试数据 (工具类:直接复制,不做修改即可!)
源码如下:
import csv
# 设计函数专门读取 csv文件
def get_csv_data(csv_file,line):
jo_csv_file= open(csv_file, 'r', encoding='utf-8-sig')
reader = csv.reader(jo_csv_file)
# 参数2 :决定了下标位置的开始计数方式
for index, row in enumerate(reader, 1):
if index == line:
print(row)
return row
if __name__=="__main__":
get_csv_data("./xuanke.csv",1)
get_csv_data("./xuanke.csv", 2)
get_csv_data("./xuanke.csv", 3)
?5.test_case(测试用例)
创建test_xuanke.py 文件:使用unittest实现测试用例
?源码如下:
import os
import unittest
import warnings
import sys
from business.xuanke_bus import jo_xuanke
sys.path.append('..')
from test_data.jotestdata import get_csv_data
class TestXUANKE(unittest.TestCase):
csv_file = os.path.abspath(os.path.dirname(os.getcwd())) + "/test_data/xuanke.csv"
def setUp(self):
print("开始测试~")
# 这句话的作用是用来忽略 ResourceWaring 异常警告的
warnings.simplefilter("ignore", ResourceWarning)
def tearDown(self):
print("结束测试~")
def test_xuanke_111(self):
print("第一条选课用例测试~")
print(self.csv_file)
data = get_csv_data(self.csv_file, 1)#获取csv文件中的第一行数据
# 预期失败用例(获取第一列和第二列数据)交给 登录
self.assertTrue(jo_xuanke(data[0], data[1],data[2]))
def test_xuanke_222(self):#获取csv文件中的第二行数据
print("第二条选课用例测试~")
data = get_csv_data(self.csv_file, 2)
# 预期失败用例
self.assertTrue(jo_xuanke(data[0], data[1],data[2]))
def test_xuanke_333(self):
print("第三条选课用例测试~")#获取csv文件中的第三行数据
data = get_csv_data(self.csv_file, 3)
# 预期失败用例
self.assertFalse(jo_xuanke(data[0], data[1],data[2]))
if __name__ == '__main__':
unittest.main()
6.test_run(测试执行器)
(1)拖入BSTestRunner.py 开源插件文件到test_run
(2)建立jwrun.py 文件:最终执行
?源码如下:
import sys
#声明包查找的路径
path=r'文件名or绝对路径'
sys.path.append(path)
from test_run.BSTestRunner import BSTestRunner
import unittest
import time
#指定测试用例和测试报告的路径
test_dir = '../test_case'
report_dir = '../joreports'
#匹配测试多条用例
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
#定义报告的文件格式
now = time.strftime("%Y-%m-%d-%H_%M_%S")
report_name = report_dir + '/' + now + 'test_report.html'
#运行用例并生成测试报告
with open(report_name, 'wb') as f:
runner = BSTestRunner(stream=f, title="我的选课功能测试报告", description="我的选课功能测试报告")
runner.run(discover)
扩展:UI一键封装步骤(双击执行)
(1)用vi joUI.sh建立脚本
(2)用i修改sh脚本,注意路径,此外jorun.py文件内的路径也相对应地一定要用文件的绝对路径
3.按esc再:wq保存并退出后,修改权限,使用chmod 777 joUI.sh
4.用./jo.sh命令行执行,如果能跑通证明双击sh文件也能跑通,这就是UI一键封装。?
7.joreports(测试报告)
?自动生成在joreports包内了,可以将文件拖入浏览器查看报告。
分割线,终于写完啦!记得在刚听到老师讲的时候,里面有些代码的细节,听得有点蒙,基础还是不够扎实,也缺乏写代码的思想,容易死磕,从中跳出来再去看代码就会更容易了,其实代码和今天学习的框架的思想是差不多的,都是层层封装一步套一步。
之前也学习过appium的自动化,那时候单纯觉得牛逼,没有去细想其中的内涵,直到今天才差不多懂了些皮毛。在听到老师说UI自动化框架就到这里时,我的心还狂跳不止,那种心情是奇妙的,因为前几天一直想着说要学习UI自动化框架,但是真的到这一步时,遇到困难,我暂时地放弃然后又继续学习,学完后感觉这东西太牛了,居然一点一点地慢慢听懂了,这是准备转行以来对这个东西最强烈地一次心动了。心想着说一定要将这个东西操作一遍,再改改,在改的过程中将其思想再记录下来不失为一件特别棒的事情。
耗时差不多两个半小时,这便是心动的代价吧。
?
|