zoukankan      html  css  js  c++  java
  • 自动化测试框架设计探索 | 从筑基期到破碎虚空

    文章过长提醒:该文跟懒婆娘的裹脚布一样——又臭又长
    【背景音乐:那女孩对我说】
    有天晚上,那女孩对你说,说她累了,不想再点点点了......
    你暗自窃喜,曾几何时听过,有个叫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() 去掉字符串末尾的
    
            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:本文涉及很多知识点,后续会一一补充,也欢迎反馈需要补充什么。
    关注公众号,回复“测试框架设计”,即可获取所有代码。

     
     
  • 相关阅读:
    final/override控制
    高效遍历图像
    快速初始化成员变量
    C++ boost.python折腾笔记
    百亿数据毫秒响应级交易系统读写分离存储数据设计
    解决VS2010子目录中的.cpp文件引用上一级目录的stdafx.h找不到定义的问题
    生产应用常见坑
    spring AOP应用
    springmvc No mapping found for HTTP request with URI in Dispatc
    myeclipse使用maven插件进行maven install时报错check $m2_home environment variable and mvn script match
  • 原文地址:https://www.cnblogs.com/mikasama/p/12692476.html
Copyright © 2011-2022 走看看