自动化测试框架设计探索 | 从筑基期到破碎虚空
文章过长提醒:该文跟懒婆娘的裹脚布一样——又臭又长
【背景音乐:那女孩对我说】
有天晚上,那女孩对你说,说她累了,不想再点点点了......
你暗自窃喜,曾几何时听过,有个叫selenium的方外之物,可模仿人操作,嗯,表现的机会来了
step 1 流水账 | 筑基期
需求:浏览器打开百度自动搜索“银魂”,并判断网页是否真的搜索到了
1、导入包
2、选择自动化浏览器,并打开百度
3、找到搜索框、输入文字、回车
4、关闭浏览器
from selenium import webdriver import time driver = webdriver.Chrome(executable_path="D:\\work\\learn_to_old\\driver\\chromedriver.exe") driver.get("https://www.baidu.com") time.sleep(3) driver.find_element_by_id("kw").send_keys("银魂") driver.find_element_by_id("su").click() time.sleep(3) # print(driver.page_source) # 判断搜索结果在不在页面中,可以用assert,也可以自己写if语句判断 if "空知英秋" in driver.page_source: print("success") else: print("fail") driver.quit()
执行,成功,漂亮!
但是,你仿佛听见有人在说你写的代码很辣鸡。作为一个半小时憋不出一行代码的人来说,你感受到了前所未有的羞辱,所以,你决定开动已经不怎么灵光的脑袋。
step 2 定义函数 | 炼气化神
1、定义函数
2、调用函数
#!/usr/bin/env python # -*- coding:utf-8 -*- from selenium import webdriver import time def search_something(): driver = webdriver.Chrome(executable_path="D:\\work\\learn_to_old\\driver\\chromedriver.exe") driver.get("https://www.baidu.com") time.sleep(3) driver.find_element_by_id("kw").send_keys("银魂") driver.find_element_by_id("su").click() time.sleep(3) source = driver.page_source if "空知英秋" in source: print("success") else: print("fial") driver.quit() search_something()
执行,依然成功,奶思!
正当你嘴角上扬,想要跟那个女孩疯狂炫耀的时候,又有个奇怪的声音传来,“如果搜索很多组东西,是不是无法做到?还是只知道copy代码?”复制?不存在的,因为就在那一瞬间,你已突破“炼气化神“境界。
step 3 数据驱动 | 炼神还虚
何谓数据驱动?通俗来讲,就是不修改函数、只变换数据,根据不同的数据,得到不同的预期值。例如,我要搜索不同的关键字,但是需要判断不同的内容会出现的页面里面,以确保结果是准确的。故而可将这两个变量作为数据,来驱动整个用例执行。大概可以用三种方式实现:1、for循环;2,ddt库;3、文本存储。(实际远远不止)
1、for循环
需求:
1.1、重定义函数,将关键字和断言内容参数化
1.2、定义循环内容,重复调用执行函数
#!/usr/bin/env python # -*- coding:utf-8 -*- from selenium import webdriver import time num = 0 fail_num = 0 suc_num = 0 def search_something(keyword, words): driver = webdriver.Chrome(executable_path="D:\\work\\learn_to_old\\driver\\chromedriver.exe") driver.get("https://www.baidu.com") time.sleep(3) driver.find_element_by_id("kw").send_keys(keyword) driver.find_element_by_id("su").click() time.sleep(3) global num, fail_num, suc_num num += 1 source = driver.page_source if words in source: print(f"搜索{keyword}成功") suc_num += 1 else: print(f"搜索{keyword}失败") fail_num += 1 driver.quit() data_list = [["银魂", "空知英秋"],["kali", "Kali Linux"], ["centos", "Red Hat"]] for i in range(len(data_list)): search_something(data_list[i][0], data_list[i][1]) print(f"共执行{num}条用例,成功{suc_num}条,失败{fail_num}条")
来来来,看看成效如何:
2、使用ddt库:
注意:因为这玩意儿只能和单元测试框架一起用,你写的时候一脸嫌弃
需求:
2.1、安装ddt库
2.2、定义数据,并调用ddt库引用
2.3、执行脚本
#!/usr/bin/env python # -*- coding:utf-8 -*- from selenium import webdriver import time import unittest from ddt import ddt,data,unpack @ddt class my_search(unittest.TestCase): def setUp(self): global num, fail_num, suc_num num = 0 fail_num = 0 suc_num = 0 # data_list = [["银魂", "空知英秋"],["kali", "Kali Linux"], ["centos", "Red Hat"]] # 注意,这边传的各组参数要单独拿开(如下),如果跟上面这样传list,ddt会认为是一组参数, # 比如@data(data_list) # 需要注意的是,每次执行一组数据,整个类是重复执行一遍的,相当于循环执行这个类 @data(["银魂", "空知英秋"],["kali", "Kali Linux"], ["centos", "Red Hat"]) @unpack def test_search(self, keyword, words): self.driver = webdriver.Chrome(executable_path="D:\\work\\learn_to_old\\driver\\chromedriver.exe") self.driver.get("https://www.baidu.com") time.sleep(3) self.driver.find_element_by_id("kw").send_keys(keyword) self.driver.find_element_by_id("su").click() time.sleep(3) global num, fail_num, suc_num num += 1 source = self.driver.page_source if words in source: print(f"搜索{keyword}成功") suc_num += 1 else: print(f"搜索{keyword}失败") fail_num += 1 def tearDown(self): self.driver.quit() print(f"本次共执行{num}条用例,成功{suc_num}条,失败{fail_num}条") if __name__ == "__main__": unittest.main()
再来执行下:
3、自定义读取文档内容作为参数
3.1 定义好函数,这个在1.1里面已经弄好
3.2 with open打开文本,该文本有数据内容,详见step3_data.txt里面的内容
3.3 将读取的内容切片组装好,循环执行search_something函数
(因为与大部分与for循环一样,这里只贴更改的部分)
# with open(file_path,"r") as f 打开某个文件,并读取,“r“表示读取,”w"表示写入,'rb'表示读二进制文件,如图片等 # 切记:先将文本设置为utf-8,在复制中文进去,然后open要用encoding with open("step3_data.txt", "r", encoding="utf-8") as f: lines = f.readlines() print(lines) for line in lines: print(line.strip()) # strip() 去掉字符串末尾的\n print(line.strip().split(",")) # 切片,用“,”来将字符串切成list keyword_list = line.strip().split(",") search_something(keyword_list[0], keyword_list[1]) print(f"共执行{num}条用例,成功{suc_num}条,失败{fail_num}条")
数据文本:
执行下:
写到这里,你忍不住暗夸自己惊才绝艳、非等闲之辈。那女孩肯定会多看你两眼。
等等,心思缜密的你盯着屏幕思考许久,还不够完美。姑娘不食人间烟火的模样还依稀浮现在你的脑海。或许可以做到类似自然语音描述测试步骤,这样那女孩即便不懂代码也能写自动化测试用例。也就是说,需要把所有的步骤都封装成函数,判断大部分可能性。
step 4 关键字驱动 还虚合道
什么是关键字驱动?简单来说,就是将想要的一些操作,封装成各种函数,当调用的时候,去匹配对应函数,从而达到简化用例描述的目的。
需求:
1、将每个步骤都定义为函数
2、在文本中描述测试步骤
3、读取步骤,并将其转为可以执行的命令(使用eval()函数)
4、执行
#!/usr/bin/env python # -*- coding:utf-8 -*- from selenium import webdriver import time import sys driver = None num = 0 fail_num = 0 suc_num = 0 def choose_driver(driver_name): global driver if driver_name == "ie": # 注意if判断语句要用== driver = webdriver.Ie(executable_path="D:\\work\\learn_to_old\\driver\\IEDriverServer.exe") elif driver_name == "chrome": driver = webdriver.Chrome(executable_path="D:\\work\\learn_to_old\\driver\\chromedriver.exe") elif driver_name == "firefox": driver = webdriver.Firefox(executable_path="D:\\work\\learn_to_old\\driver\\firefox.exe") else: print("选择的浏览器驱动有误,请重新选择!") sys.exit() def open_url(url): global driver driver.get(url) def send_keywords(find_way, ele, keyword): ele = find_ele(find_way, ele) ele.send_keys(keyword) def click(find_way, ele): ele = find_ele(find_way, ele) ele.click() def sleep(sleep_seconds): time.sleep(int(sleep_seconds)) # 封装元素查找方法,如果想获取list,则用find_elements_by_name之类 # 也可以用js来定位 driver.execute_script("document.getElementById('username')") def find_ele(find_way, ele): global driver if find_way == "id": ele = driver.find_element_by_id(ele) # find_str = find_element_by_id elif find_way == "name": ele = driver.find_element_by_name(ele) elif find_way == "xpath": ele = driver.find_element_by_xpath(ele) elif find_way == "link_text": ele = driver.find_element_by_link_text(ele) elif find_way == "partial_link_text": ele = driver.find_element_by_partial_link_text(ele) elif find_way == "tag_name": ele = driver.find_element_by_tag_name(ele) elif find_way == "class_name": ele = driver.find_element_by_class_name(ele) elif find_way == "css_selector": ele = driver.find_element_by_css_selector(ele) else: print("元素查找方式有误,请修正!") sys.exit() return ele def my_assert(words): global driver, num, suc_num, fail_num num += 1 source = driver.page_source if words in source: print(f"搜索{words}成功") suc_num += 1 else: print(f"搜索{words}失败") fail_num += 1 def quit(): global driver driver.quit() # 2、使用步骤 # with open(file_path,"r") as f 打开某个文件,并读取,“r“表示读取,”w"表示写入,'rb'表示读二进制文件,如图片等 # 切记:先将文本设置为utf-8,在复制中文进去,然后open要用encoding # 有两种方法可以将["open","xxx"]组装成'open("xxx")' # 1、先定义模板str,使用replace()函数替换即可 # 2、直接使用格式化字符串方式替换,这里使用的是f"eeee{xxx}",其他%s或者format也可以,参考订阅号上次发布的文章 with open("step4_steps.txt", "r", encoding="utf-8") as f: lines = f.readlines() # strs = "do_something()" 暂时不用此方法 for line in lines: # print(type(line)) # 根据line.count('|')来判断,步骤可以更加简化 if "|" in line: eval_str_list = line.strip().split("|") # print("===========",eval_str_list) if len(eval_str_list) == 2: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}')" elif len(eval_str_list) == 3: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}')" elif len(eval_str_list) == 4: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}')" elif len(eval_str_list) == 5: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}', '{eval_str_list[4]}')" elif len(eval_str_list) == 6: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}', '{eval_str_list[4]}', '{eval_str_list[5]}')" else: print("过长,需要优化程序") print(eval_str_list,"*"*50) print(eval_str) eval(eval_str) else: # eval_str = strs.replace("do_something",line.strip()) eval_str = f"{line.strip()}()" print(eval_str) eval(eval_str) print(f"共执行{num}条用例,成功{suc_num}条,失败{fail_num}条")
用例描述为文本:
choose_driver|chrome open_url|https://www.baidu.com sleep|3 send_keywords|class_name|s_ipt|银魂 click|xpath|//*[@id="su"] sleep|3 my_assert|空知英秋 quit
执行结果:
嗯,又向前跨了一大步,你端着咖啡微眯着眼,虽然依稀能感受到头顶传来的阵阵凉意。
再向前跨一步吧,刚刚数据处理方式还是可以套用的,你放下手中的咖啡,摸了摸头发,又开始了。
step 5 混合驱动 | 破碎虚空
需求:
1、构造数据替换把位
2、使用正则将数据替换掉把位
3、执行
(因为大部分跟关键字驱动一样,这里只贴了部分,将open语句封装,以及后面部分逻辑)
def read_file_to_line(file_path): with open(file_path, "r", encoding="utf-8") as f: lines = f.readlines() return lines test_datas = read_file_to_line("step5_data.txt") #读取测试数据 for test_data in test_datas: test_data = eval(test_data) # 将字符串转换为json,就不需要json.dump啥的了 test_steps = read_file_to_line("step5_steps.txt") # 读取测试步骤 for test_step in test_steps: # 找到关键字“{{”,替换数据 if "${" in test_step: # print("#"*10,type(test_step)) # 使用正则找到变量,$前面要加\,即便是字符串前用了r key = re.search(r"\${(.*?)}", test_step).group(1) # print("==== key ======",key) # {{key}} value step 替换数据 test_step = re.sub(r"\${%s}" %key, test_data[key], test_step) # print("#"*10,test_step) # print(type(test_step)) # 根据line.count('|')来判断,步骤可以更加简化 if "|" in test_step: eval_str_list = test_step.strip().split("|") # print("===========",eval_str_list) if len(eval_str_list) == 2: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}')" elif len(eval_str_list) == 3: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}')" elif len(eval_str_list) == 4: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}')" elif len(eval_str_list) == 5: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}', '{eval_str_list[4]}')" elif len(eval_str_list) == 6: eval_str = f"{eval_str_list[0]}('{eval_str_list[1]}', '{eval_str_list[2]}', '{eval_str_list[3]}', '{eval_str_list[4]}', '{eval_str_list[5]}')" else: print("过长,需要优化程序") print(eval_str_list,"*"*50) else: # eval_str = strs.replace("do_something",test_step.strip()) eval_str = f"{test_step.strip()}()" print(eval_str) # 异常处理 try: eval(eval_str) except Exception as e: print(f"{e}") print(f"共执行{num}条用例,成功{suc_num}条,失败{fail_num}条")
带把位的步骤:
choose_driver|chrome open_url|https://www.baidu.com sleep|3 send_keywords|class_name|s_ipt|${keyword} click|xpath|//*[@id="su"] sleep|3 my_assert|${assert_words} quit
数据:
{"keyword":"银魂", "assert_words":"空知英秋"} {"keyword":"kali", "assert_words":"Kali Linux"} {"keyword":"centos", "assert_words":"Red Hat"}
执行:
perfect!!!此刻你感觉人生已经到达巅峰了,生物本能驱动你立马寻找那个女孩,只是,四下望去,哪有什么女孩......
PS:本文涉及很多知识点,后续会一一补充,也欢迎反馈需要补充什么。
关注公众号,回复“测试框架设计”,即可获取所有代码。