美文网首页
100行代码打造关键字驱动的ui自动化测试框架

100行代码打造关键字驱动的ui自动化测试框架

作者: Null_ice | 来源:发表于2018-07-26 16:55 被阅读0次

    本篇旨在通过一个基础demo的实现讲解,提供一个关键字驱动框架实现方案的思路指引。

    为什么要实现关键字驱动框架?

    其实之前已经介绍过纯代码方式的自动化框架实现,那为什么还要去进行关键字框架的实现呢?

    纯代码方式的话,门槛较高,用户需要先掌握对应的语法才可以进行编写,如果团队里缺乏这样的角色,自动化测试就会难以进行,因此如果我们做成关键字的形式,那么用例都只是关键字的拼装,就会大大减少学习门槛;同时,基于关键字的用例风格较为统一,方便用例阅读和管理。

    当然市面上其实已经有一些成熟的关键字驱动框架了,较为人知的比如robotframework,为什么不直接采用rf框架呢?

    rf框架的话,安装较为繁琐复杂,而且不方便扩展成web平台;编写用例依赖Ride不太方便管理和传阅;另一点的话,就是我个人希望重复造个轮子来感受一下(造轮子也是个很重要的学习过程)。

    定义用例的形式

    在我们实现这个框架之前,我们先来想一下这个用例的形式应该是什么?

    传统的代码写用例,基本都是采用po模式,用例继承maincase,maincase继承unittest.testcase,但无论如何,一个用例流程下来,不考虑复杂情况的话,无非只需要三个元素:动作,对象,以及参数;

    这里我直接给出一个简单的用例构造,

    cases =[
    [
        {'name': 'test_baidu'},
        {'action': "打开网址", 'parmeters':['http://www.baidu.com/']},
        {'action':"输入", 'parmeters':["百度搜索框",'100行代码打造关键字驱动的ui自动化测试框架']},
        {'action': "点击", 'parmeters': ["百度搜索按钮"]},
        {'validate': "页面文本包含", 'parmeters': ["百度"]},
    ],
    
    ]
    
    

    当然这个是抽象成py对象的方式展现的,在实际的工作里,你完全可以写的更加“脱离代码化”,例如放到yml文件里,然后通过解析函数解析成以上构造,这样用例看起来就和代码完全不沾边了;这里为了不让情况变得复杂,所以直接采用py对象的方式来处理用例。

    在用例的定义里,每个用例都是一个列表,由一个个字典对象组成;第一个字典存储该用例的名称,往下每个字典存储动作(action)或者是校验点(validate),而每个action和validate里,都含有一个parmeters的列表存储他们需要的参数。

    因此不难看出,如果我们想要这个用例可以运行,那么可以把每个action或validate当做一个函数,而每个parmeters当做这个函数的参数,这样顺序的执行下来,就可以完成我们每个动作以及最后的校验。

    那么具体我们要怎么做呢?

    关键字映射和反射

    前面说了,这个东西我最终是希望成为一个web平台的,因此在这个demo的实现里,我新建了一个叫"keyword_db.py"的文件模拟关键字映射数据库,这个文件用以存储中文的动作(action)和 校验点(validate)关键字,和这个关键字实际对应的函数名称,来实现动作关键字和函数的映射,这个文件的内容大概是这样:

    keyword = {
        "输入": 'input',
        "点击": 'click',
        "打开网址": 'open_url',
        "等待":'wait_time',
        "元素文本包含": 'element_text_has',
        "页面文本包含": 'page_text_has',
    
    
    }
    

    字典的key实际上就是中文的动作或者校验点,而对应的value就是实际的函数名称,这样我们初步的映射就完成了,但是仅仅这样是无法让用例动起来的,我们还需要进行处理让程序处理用例的时候,可以执行到关键字对应的函数。

    第一步,写出你关键字对应的函数:

    既然要执行关键字对应的函数,那么第一步肯定是需要存在这么一个函数;我新建了一个"function.py"的文件用于存储我们的关键字对应的函数,在这个文件里写出实现函数的具体代码,截取其中的一些展示一下:

    def find_element(element,driver):
        webelement = driver.find_element(*element)
        return webelement
    
    def open_url(url,driver):
        driver.get(url)
    
    def input(element,str,driver):
        find_element(element, driver).send_keys(str)
    
    def page_text_has(str, driver):
        page_text = driver.page_source
        return unittest.TestCase().assertIn(str,page_text,"页面文本不包含预期值!")
    

    这样,我们的第一步就完成了。

    第二步,通过反射拿到函数地址:

    当我们解析了用例,拿到关键字和函数的映射关系后,例如,通过解析关键字"输入",在keyworddb里找到了"输入"对应的函数名称input(注意,这里其实是拿到了input这个字符串而不是真正的函数地址),这时候我们通过反射机制,在function.py里通过getattr()方法查找input这个字符串,可以拿到input这个字符串对应的function地址,我们就可以存下这个地址用来执行input这个函数了。

    讲起来可能比较绕口,这里举一个例子,比如有一个A.py的模块里,有一个方法printA:

    def printA():
        print("AAAAAAAAA")
    

    我们再新建一个B.py的模块,在里面用getattr()方法拿到A里的这个printA这个方法执行:

    import A
    func = getattr(A, "printA")
    func()
    

    输出结果是"AAAAAAAAA",可以看到,我们没有直接执行printA方法,而是通过在A模块里查找"printA"这个字符串对应的函数,存到func变量里执行,一样实现了执行printA函数,这就是反射机制。

    回到我们的demo里来,通过反射机制在function模块里查找对应的函数名称,我们就把真正的函数地址存了起来,和上面反射的例子不同的是,我们的函数大多需要一个或者多个参数,如何处理参数呢?

    元素仓库和函数参数处理

    既然我们选择了关键字驱动,自然在元素的书写上肯定不能还是纯代码式的find_element_by_XXX(xxx)的方式,这里我建了一个"element_db.py"的文件模拟数据库存储元素作为元素仓库。

    大概内容如下:

    element = {
        '百度搜索框':('By.ID', 'kw'),
        '百度搜索按钮': ('By.ID', 'su'),
    }
    
    

    可以看出和关键字映射是一个套路,就是把中文的元素名称和一个查找关系做映射,key是中文的元素名称,value是一个元祖,包含查找方式和值。

    这里有个问题,看到前面的用例构造可以看出,我们会用中文的元素名称作为函数的参数,这样就需要对参数进行处理,里面存在一个逻辑,如果这个参数是元素类型的参数,就需要先从元素仓库找出他的对应查找方式,再转换为真正的element对象作为函数的参数,而其他类型的参数就直接用原值,无需转换。

    如何确定哪个参数是元素类型的参数呢?我们通过inspect.signature()方法,可以拿到函数的参数名称,例如,前面我们写了一个函数:

    def find_element(element,driver):
        webelement = driver.find_element(*element)
        return webelement
    

    那么执行以下代码,就可以拿到函数的参数名称:

    parmeters = inspect.signature(find_element).parameters.values()
    

    结果是[element,driver],之后我们通过对参数名称进行分析,就可以知道哪些参数需要转换,哪些不用(当然这么做对函数参数的书写规则就要有要求)。

    以上动作我们既拿到了函数地址,又处理了对应的函数参数,是不是就可以执行了呢?

    测试框架和用例工厂

    当然你直接执行是没问题的,但是如果不用测试框架处理,就无法得到测试框架的便利性例如自动生成的测试报告等等。

    我们选用了unittest框架作为示例,unittest框架写用例一般就是三部分,setup准备,test_开头的方法进入测试步骤,然后结束用teardown清理,怎么把这些融入到我们的用例里呢?

    这里我提出了一个用例工厂的概念,实际上是个方法,主要负责对我们从用例解析出的函数和对应参数进行包装,产生一个又一个符合unittest格式的用例。

    这个也不复杂,主要使用了type()这个方法,type()很多人可能用它去查看变量的数据类型,但其实他还可以创造类型比如创造一个类,例如如下代码:

    testcase = type("TESTCASE",(unittest.TestCase,),{'tearDown':teardown,'setUp':setup},)
    

    这段代码其实就是创造了一个叫做TESTCASE的类,继承自unittest.TestCase,类里有两个事先定义好的方法teardown和setup。

    我们把这个类用unittest中testsuite的add方法处理一下,就可以得到一个标准的unittest的testcase了。

    以上就是我们的全部内容,至此,这个关键字驱动的ui自动化测试框架demo就打造好了。

    一些说明

    这个demo仅仅是个思路指引,当然你也可以直接用,不过还有很多没有完善和很死板的地方需要去修补,之后ok了,我会再放上用这个框架为核心打造的web平台版。

    想运行这个demon,你需要:
    py3的环境
    selenium库
    HTMLTestRunner_PY3(放到py3目录下的lib目录里)
    适合你chrome版本的chromedriver(放到py3的根目录下)

    在case模块编写用例,执行process模块执行用例,测试报告在report目录下。

    Git地址

    https://github.com/icesword0760/uitest-keyword

    相关文章

      网友评论

          本文标题:100行代码打造关键字驱动的ui自动化测试框架

          本文链接:https://www.haomeiwen.com/subject/bdgbmftx.html