• Robot Framework自定义测试库开发详解


    Robot Framework是一个开源自动化测试框架,主要特点是基于关键字驱动,本文介绍自定义测试库及测试库文档创建方法。

    简介

    Robot Framework是用于验收测试(Acceptance Testing),验收测试驱动开发(Acceptance Test Driven Development, ATDD)和
    机器人流程自动化(Robotic Process Automation, RPA)的开源自动化框架,支持数据驱动、关键字驱动和行为驱动(BDD)。它具有简单的纯文本语法,并且可以使用Python或Java实现库的扩展。

    Robot Framework核心框架使用Python实现,支持Python 2 和 Python 3,还可以在Jython(JVM),IronPython(.NET)和PyPy上运行。该框架具有丰富的生态,有各种通用库和工具,比如selenium测试库SeleniumLibrary,appium测试库AppiumLibrary等,更多测试库可查看官网http://robotframework.org

    Robot Framework GitHub地址:https://github.com/robotframework/robotframework

    RIDE地址:https://github.com/robotframework/RIDE

    下图是基于Robot Framework进行web自动化(SeleniumLibrary)测试的架构图,

    https://blog.codecentric.de/en/2012/04/robot-framework-tutorial-a-complete-example/

    安装:

    pip install robotframework
    pip install robotframework-ride
    

    RIDE是使用wxPython库编写的Robot Framework图形界面。

    RF分层思想

    一个好的设计模式有助于提高脚本开发速度,且利于维护。使用Robot Framework进行自动化用例开发时可以考虑使用分层的思想,类似于Page Object 设计模式。

    顾名思义,“分层” 意思就是把一个过程分为多层,比如下面的例子:实现web UI自动化的登录测试。

    一共分为了4层:

    • 01_测试用例:编写测试用例
    • 02_流程层: 封装操作流程关键字
    • 03_元素层:存放页面元素标签以及页面基本操作关键字,比如开关操作,点击,输入等
    • 04_数据:用来存放数据,比如全局变量

    使用分层思想,将数据和流程分离开,能够减少一些由需求变更引发的修改,更易于扩展和维护。当然也可以使用其它分层方式,最终目的是让你的项目容易维护。

    Robot Framework的测试库已经非常丰富了,如果还是无法满足需求,可以自己开发,下面来介绍如何开发自定义测试库。

    开发自定义测试库

    Robot Framework主要有三种API:静态API、动态API和混合API,下面分别进行介绍。

    静态API

    静态API直接将方法(methods)映射为关键字。关键字接收的参数和方法相同的参数一样。

    1. 编写测试库

    ~\Lib\site-packages目录中新建一个包【比如MyLibrary】, 然后在创建的测试库MyLibrary目录下新建MyKeywords类文件(MyKeywords.py),用来封装要实现的关键字。

    class MyKeywords():
        def test_add(self, a, b, c):
            """两数相加
    
            :a: value1
    
            :b: value2
    
            :c: 预期结果
    
            Example:
            | Test Add | 2 | 3 | 5 |
    
            """
            if float(a) + float(b) == float(c):
                return True
            else:
                raise RuntimeError("%s + %s != %s"%(a,b,c))
    

    接下来在~\Lib\site-packages\MyLibrary 目录下新建version.py 文件,用于设置自定义测试库MyLibrary的版本信息。

    # -*- coding:utf-8 -*-
    VERSION = '0.0.1'
    

    然后在__init__.py文件导入关键字及版本信息。

    # -*- coding:utf-8 -*-
    from .MyKeywords import MyKeywords
    from .version import VERSION
    _version_ = VERSION
    class MyLibrary(MyKeywords):    
        """    MyLibrary    """    
        ROBOT_LIBRARY_SCOPE = 'GLOBAL'
    

    2. 导入测试库

    创建一个测试套件,然后导入测试库

    查询测试库关键字信息(F5)

    3. 编写用例脚本

    *** Settings ***
    Library           MyLibrary
    
    *** Test Cases ***
    case_001
        ${sum}    Test Add    2    3    5
        Should Be True    ${sum}
    

    动态API

    动态API和静态API的区别在于发现测试库关键字、关键字的参数、说明文档,以及关键字实际执行方式不同。Python版本中,静态API是使用反射的方式获取(python反射介绍可参考Python反射介绍),而动态API采用的是指定的方法来动态获取这些信息,主要包括4个方法:get_keyword_names、run_keyword、get_keyword_arguments和get_keyword_documentation 。这些方法其实也使用了Python反射函数来获取测试类属性信息。

    使用动态API的好处就是可以更加灵活的编写测试库,动态API说明文档可参考:http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dynamic-library-api

    1. 编写测试库

    和静态API类似,在~\Lib\site-packages目录中新建一个包【MyDynamicLibrary】, 然后在创建的测试库MyDynamicLibrary目录下新建MyKeywords类文件(MyKeywords.py),用来封装要实现的关键字。

    from robot.api.deco import keyword
    from robot.api import logger
    
    class MyKeywords(object):
        def __init__(self):
            self._cal = Calculate()
    
        def get_keyword_names(self):
            # 获取当前测试类的所有属性
            attributes = [(name, getattr(self._cal, name)) for name in dir(self._cal)]
            # 过滤没有设置robot_name的属性
            keywords = [(name, value) for name, value in attributes
                        if hasattr(value, 'robot_name')]
            # 返回关键字名称
            return [value.robot_name or name for name, value in keywords]
    
        def run_keyword(self, name, args, kwargs):
            print("Running keyword '%s' with positional arguments %s and named arguments %s."
                  % (name, args, kwargs))
            func = getattr(self._cal, name)
            return func(*args,**kwargs)
    
    
    class Calculate(object):
        
        def not_keyword(self):
            pass
    
        @keyword
        def test_sub(self, a, b, results=None):
            """两数相减
            """
            if float(a) - float(b) == float(results):
                return True
            else:
                raise RuntimeError("%s - %s != %s"%(a,b,results))
    
        @keyword
        def test_add(self, a, b, c):
            """两数相加
    
            :a: value1
    
            :b: value2
    
            :c: 预期结果
    
            Example:
            | Test Add | 2 | 3 | 5 |
    
            """
            if float(a) + float(b) == float(c):
                return True
            else:
                raise RuntimeError("%s + %s != %s"%(a,b,c))
    

    get_keyword_names 方法实现动态获取关键字名称,此方法没有参数,并且必须返回包含当前测试库实现的关键字列表或数组。代码中使用到了python反射函数 getattr()hasattrdir()

    run_keyword 用于执行关键字,包括2个或者3个参数:name, args, kwargs,name 为关键字方法名称,通过get_keyword_names 获得;args 为关键字方法的位置参数,kwargs为关键字参数,可以不设置。

    get_keyword_namesrun_keyword 是动态库必须要有的方法,如果没有它们就会变成一个静态库。动态API涉及到的主要方法如下表。

    方法 参数 说明
    get_keyword_names 获取关键字名称
    run_keyword name, arguments, kwargs 执行关键字
    get_keyword_arguments name 获取关键字参数(可选方法)
    get_keyword_types name 返回关键字参数类型信息(可选方法)
    get_keyword_tags name 返回关键字标签(可选方法)
    get_keyword_documentation name 返回关键字及测试库文档(可选方法)
    get_keyword_source name 返回关键字源信息(可选方法)

    Calculate是一个存放关键字的类,这个类可以写在其它模块中。

    __init__.pyversion.py文件和静态API写法一样。

    2. 导入测试库

    导入测试库MyDynamicLibrary后,查询测试库关键字信息(F5)

    从上图可以看到已经获取到了关键字,没有添加 @keyword 装饰器的not_keyword 关键字没有显示出来。

    另外,Description那一列是空白的,Arguments没有显示关键字的真实参数,关键字注释文档也没有显示。这需要get_keyword_documentationget_keyword_arguments 这两个函数来实现文档和变量显示。将下面代码添加到MyKeywords类中:

    def get_keyword_documentation(self, name):
        func = getattr(self._cal, name)
        return func.__doc__
    
    def get_keyword_arguments(self, name):
        func = getattr(self._cal, name)
        func_args = func.__code__.co_varnames
        if func_args[0] == "self":
            func_args = func_args[1:]
            return func_args
    

    重启Robot Framework,F5查看测试库关键字信息:

    3. 编写用例脚本

    *** Settings ***
    Library           MyDynamicLibrary
    
    *** Test Cases ***
    case_001
        ${res}    Test Add    2    3    5
        Should Be True    ${res}
    
    case_002
        ${res}    Test Sub    6    1    results=5
        Should Be True    ${res}
    

    执行,日志如下:

    Starting test: PO demo.01 测试用例.自定义测试库-动态API.case_001
    20211119 18:00:00.980 :  INFO : Running keyword 'test_add' with positional arguments ('2', '3', '5') and named arguments {}.
    20211119 18:00:00.980 :  INFO : ${res} = True
    Ending test:   PO demo.01 测试用例.自定义测试库-动态API.case_001
    
    Starting test: PO demo.01 测试用例.自定义测试库-动态API.case_002
    20211119 18:00:00.982 :  INFO : Running keyword 'test_sub' with positional arguments ('6', '1') and named arguments {'results': '5'}.
    20211119 18:00:00.982 :  INFO : ${res} = True
    Ending test:   PO demo.01 测试用例.自定义测试库-动态API.case_002
    

    动态库也可以借助robotlibcore 库来实现,编写动态API更加简洁。具体使用方法参考:https://github.com/robotframework/PythonLibCore

    混合API

    混合API就是静态API和动态API方式的混合使用。混合API通过使用 get_keyword_names 方法获取所有关键字,但是不使用 run_keyword 方法执行关键字,执行方式和静态API相同。

    ~\Lib\site-packages目录中新建一个包【MyHybridLibrary】, MyKeywords.py文件中编写关键字方法,使用 get_keyword_names 方法获取关键字,其它写法和静态API类似:

    from robot.api.deco import keyword
    from robot.api import logger
    
    class MyKeywords(object):
        def get_keyword_names(self):
            # 获取当前测试类的所有属性
            attributes = [(name, getattr(self, name)) for name in dir(self)]
            # 过滤没有设置robot_name的属性
            keywords = [(name, value) for name, value in attributes
                        if hasattr(value, 'robot_name')]
            # 返回关键字名称
            return [value.robot_name or name for name, value in keywords]
    
        def not_keyword(self):
            pass
    
        @keyword
        def test_sub(self, a, b, results=None):
            """两数相减
            """
            if float(a) - float(b) == float(results):
                return True
            else:
                raise RuntimeError("%s - %s != %s"%(a,b,results))
    
        @keyword
        def test_add(self, a, b, c):
            """两数相加
    
            :a: value1
    
            :b: value2
    
            :c: 预期结果
    
            Example:
            | Test Add | 2 | 3 | 5 |
    
            """
            if float(a) + float(b) == float(c):
                return True
            else:
                raise RuntimeError("%s + %s != %s"%(a,b,c))
    

    __init__.pyversion.py文件和静态API写法一样。

    导入测试库MyHybridLibrary后,查询测试库关键字信息(F5)

    导入Python脚本

    如果不想创建包,可以直接写py文件,然后导入。

    可以将测试库文件放在 ~\Lib\site-packages 目录下,这样在导入 Library 的时候可以直接写库名称就可以了,如果放在其它地方,需要使用绝对路径或者相对路径。

    需要注意的是测试类名称要和文件名一致,比如编写测试库TestLlibrary.py:

    class TestLlibrary():
        def __init__(self):
            pass
    
        def test_sub(self, a, b, results=None):
            """两数相减
            """
            if float(a) - float(b) == float(results):
                return True
            else:
                raise RuntimeError("%s - %s != %s"%(a,b,results))
    

    这种方式和静态API的关键字读取、文档显示、关键字执行一样,其实它就是静态API。

    生成测试库文档

    可以将关键字方法的document注释(3个双引号括起来的内容)提取出来生成一个测试库文档。使用Libdoc工具可以很方便生成一份测试库文档,命令格式:

    python -m robot.libdoc -f html MyLibrary MyLibrary.html
    

    生成MyHybridLibrary库文档:

    $ python -m robot.libdoc -f html MyHybridLibrary MyHybridLibrary.html
    D:\MyHybridLibrary.html
    

    MyHybridLibrary.html内容:

    这非常方便,不需要另外编辑一份测试文档,更新关键字后,执行上面的命令就可以快速生成。

    参考文档:

    1. https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html
    2. https://github.com/robotframework/PythonLibCore
    --THE END--
  • 相关阅读:
    安装MongoDB
    power mode idea 插件
    安装nodejs,运行打包Vue项目
    我的爬虫随笔(一)
    用sql实现背包问题
    HTML基础
    CSS配置颜色和文本
    MongoDB技术实践与应用案例征集活动
    7、消息队列的高可用、高可靠
    运行asp.net core webapi 时报502错误
  • 原文地址:https://www.cnblogs.com/hiyong/p/15582332.html
Copyright © 2020-2023  润新知