• 使用Flask搭建基于unittest的简单用例挑选及执行平台


    在用例组织上,unittest的Test Suite的拥有非常好的灵活性,然而Test Suite一般要提前编制好,添加和组织用例必须使用代码,不方便使用。
    本文使用 Flask + unittest.TestSuite + pickle搭建一个简单的unittest用例挑选和执行平台。

    思路:
    添加Test Suite: 使用discover()发现所有测试用例 -> 挑选用例 并生成Test Suite对象 -> 使用pickle.dump()序列化成文件 保存
    执行Test Suite: 使用os.listdir()得到Test Suite列表 -> 使用pickle.load()反序列化成TestSuite对象 -> 执行TestSuite并生成报告

    需要安装 Flask, pip install flask

    在你的测试框架或用例同级目录下建立dashboard目录,结构如下

    - dashboard
      - suites  序列化的Test Suite文件目录
      - templates  页面模板
      - app.py
    - test
      - case 用例目录
    

    新建app.py作为我们的接口服务文件
    这里我们只实现3个页面:

    1. 添加用例 suite_add
    2. 用例列表(可以选择suite执行)suite_list
    3. 报告页面 report

    使用Flask写接口(页面)的大致套路

    # 1. 导入包
    from flask import Flask  # Flask类,使用flask框架的基本类
    from flask import request  # 请求对象,用于获取请求参数
    from flask import render_template # 用于渲染模板,并返回客户端一个html页面
    from flask import redirect # 用于跳转到其他url
    
    # 2. 实例化Flask类
    app = Flask(__name__)  # 使用当前模块实例化一个Flask对象,app是自定义的变量(后面使用要一致)
    
    # 3. 编写接口(页面)并挂载访问地址,指定允许的请求方法
    @app.route("/suite_add", methods=["GET", "POST"]  # 接口(页面)地址为/suite_add, 支持GET和POST
    def suite_add():
        if request.method == 'POST':  # POST方法用来处理添加
            .....
            return redirect("/")  # 添加后跳转到 首页
        return render_template("suite_add.html")  # 如果是GET方法,渲染返回suite_add页面
    
    # 4. 运行调试
    if __name__ == '__main__':  
        app.run()
    

    suite_add这个接口的实现逻辑为:

    1. 使用unittest的discover遍历并抽取所有用例返回给客户端(GET请求)
    2. 获取到客户端挑选的用例并生成unittest的TestSuite对象
    3. 根据客户端传的TestSuite名称,序列化成指定名称的文件并保存

    代码如下:

    from flask import Flask  # Flask类,使用flask框架的基本类
    from flask import request  # 请求对象,用于获取请求参数
    from flask import render_template # 用于渲染模板,并返回客户端一个html页面
    from flask import redirect # 用于跳转到其他url
    import os  # 用于组装绝对路径
    import unittest  
    import pickle  # 序列化方法
    
    # 一些要使用到的目录绝对路径
    app_dir = os.path.dirname(os.path.abspath(__file__))  # dashborad目录
    case_dir = os.path.join(os.path.dirname(app_dir), 'test', 'case')  # 测试用例目录
    suite_dir = os.path.join(app_dir, 'suites')  # 测试套件目录
    
    def collect():  # 收集用例并组成Test Suite
        suite = unittest.TestSuite()  # 新建Test Suite
    
        def _collect(tests):  # 由于unittest discover得到的TestSuite中包含了目录及子目录的路径,这里只把所有的用例抽取出来
            if isinstance(tests, unittest.TestSuite):
                if tests.countTestCases() != 0:
                    for i in tests:
                        _collect(i)
            else:
                suite.addTest(tests)
    
        _collect(unittest.defaultTestLoader.discover(case_dir))
        return suite
    
    app = Flask(__name__)  # 使用当前模块实例化一个Flask对象,app是自定义的变量(后面使用要一致)
    
    @app.route("/suite_add", methods=["GET", "POST"]  # 接口(页面)地址为/suite_add, 支持GET和POST
    def suite_add():
        tests = []  # 用例集合
        for case in collect():
            tests.append(case.id())  # 遍历testsuite中的用例,通过case.id()拿到用例名
    
        if request.method == 'POST':  # POST方法用来处理添加
            suite_name = request.form.get("suite_name")   # 从前端获取需要新建的testsuite名称
            cases = request.form.getlist("cases")  # 获取到前端选择的用例列表
            suite = unittest.defaultTestLoader.loadTestsFromNames(cases)  # 通过用例名列表生成TestSuite
    
            with open(os.path.join(suite_dir, suite_name+".testsuite"), 'wb') as f:  # 序列化并保存TestSuite
                pickle.dump(suite, f)
    
            return redirect("/")  # 添加后跳转到 首页
        return render_template("suite_add.html")  # 如果是GET方法,渲染返回suite_add页面
    
    if __name__ == '__main__':  # 运行接口方法
        app.run()
    

    templates/suite_add.html代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>新增Test Suite</title>
    </head>
    <body>
    <h1>新增Test Suite</h1>
    <form action="#" method="post">
        标题:<input type="text" name="suite_name">
        <h4>选择用例</h4>
        {% for test in tests%}
            <div><input type="checkbox" id="cases" name="cases" value="{{test}}">{{test}}</div>
        {% endfor %}
        <div><input type="submit" value="保存"></div>
    </form>
    </body>
    </html>
    

    执行后访问 http://127.0.0.1:5000/suite_add
    新增Test Suite
    标题输入suite1,选择用例,点击添加会在dashboard/suites下生成suite1.testsuite文件
    suite序列化文件

    由于添加后跳转到"/",这个接口(页面)还没实现,所以会报错,我们现在来实现suite列表, 思路:

    1. 使用os.listdir()遍历suites目录下的.testsuite文件并渲染到页面上

    在app.py中添加

    from HTMLTestReportCN import HTMLTestRunner # 这个是生成html报告的文件,放在app.py同级即可
    
    @app.route("/", methods=['GET', 'POST'])
    def suite_list():
        suite_list = [suite.split(".")[0] for suite in os.listdir('suites') if suite.endswith(".testsuite")]  # 遍历并去掉.testsuite扩展名
        if request.method == 'POST':  # 执行testsuite方法
            suite_name = request.form.get("suite")
            import sys;sys.path.append(case_dir)  # 反序列化必须将 用例目录添加到sys.path中
            with open(os.path.join(suite_dir, suite_name+".testsuite"), 'rb') as f:  # 反序列化并得到TestSuite对象
                suite = pickle.load(f)
    
            with open(report_file, 'wb') as f:  # 执行TestSuite并在templates目录中生成html文件
                result = HTMLTestRunner(stream=f, title="Api Test", description="测试描述", tester="卡卡").run(suite)
            return redirect("/report")  # 显示报告
    
        return render_template('suite_list.html', suite_list=suite_list)  # GET方法返回suite列表页面
    

    suite_list页面代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <h1>Test Suite列表</h1>
    <a href="/suite_add">添加Test Suite</a>
    <br>
    <form action="#" method="post">
      {% for suite in suite_list %}
      <input type="radio" name="suite" value="{{suite}}">{{ suite }}
      <br>
      {% endfor %}
      <input type="submit" value="执行">
    </form>
    
    </body>
    </html>
    

    testsuite列表页面

    显示报告方法(报告必须生成在templates目录下)

    @app.route("/report", methods=['GET'])
    def report():
        return render_template('report.html')
    

    执行某个testsuite后会跳转到报告页面
    报告页面

    完整代码:api_test_framework

    更多学习资料请加添加作者微信:lockingfree获取

  • 相关阅读:
    跨域访问(nginx)
    mybatis中<set>标签和<trim prefix="set" suffixOverrides=",">
    如何在Oracle中复制表结构和表数据
    Oracle删除重复数据
    Oracle中删除表
    Oracle中on和where的区别
    Java实体类中的类型对应mybatis的jdbcType
    Java 枚举(enum) 常见用法
    关于JS 事件冒泡和onclick,click,on()事件触发顺序
    浅析 SpringMVC 的@PathVariable 和 @RequestParam 和 @RequestBody注解
  • 原文地址:https://www.cnblogs.com/superhin/p/11454773.html
Copyright © 2020-2023  润新知