• QTA-qtaf自动化测试实践


    背景:

    • 项目leader认为万物皆可自动化,然后就拍脑袋要搞个自动化,后面搞不下去了,凉了。虽然说项目凉了,但是过程中收获满满,所以脱敏后就有了这篇博客:https://www.cnblogs.com/Klay/p/15339223.html

    需求:

    • 需要把自动化做成一个服务,提供给后台调用
    • 对测试进度量化,如:测试进度的百分比
    • 测试报告定制化,如:展示一个测试用例信息、测试标题、测试步骤等
    • 可以在前端选择执行哪些用例,支持单个用例执行。

    隐藏需求:

    • 我们要自己写用例
    • 执行用例的不是我们

    概要设计

    • 测试对象:http/https接口、web系统(后续方向)、app等(后续方向),基于此平台设计的自动化框架要包含测试用例管理、测试执行控制、测试报表及测试日志的生成,整体测试框架要轻量易用。
    • 整个服务分两个部分,一是部分提供http接口对接外部服务,这里就不详述了,二是提供自动化部分。
    • 自动化部分总共分为5个公共库模块(可复用函数、日志管理)、用例仓库(具体用例的管理)、页面管理(单独对Web页面或接口进行抽象,封装页面元素和操作方法)、执行模块(测试计划和执行计划)以及静态资源,模块之间不是相互孤立的,而是相辅相成的。

    详细设计

    • 用 qtaf 来管理用例,基础详情可以看 官方文档
    • 然后基础功能都有了。官方文档比较详细,这里就不复制粘贴了。

    拓展 testplan

    拓展测试计划主要可以定制测试环境、全局初始化和测试数据等。

    点击查看代码
    
    from testbase.loader import TestLoader
    from testbase.plan import TestPlan
    from testbase.runner import TestRunner
    
    
    class XxxxTestPlan(TestPlan):
        tests = TestLoader().load("testcase.demo")
        test_target_args = ""
    
        hosts = ""
    
        def get_test_target(self):
            """获取被测对象详情"""
            return self.tests
    
        def test_setup(self, report):
            """全局初始化"""
            pass
    
        def resource_setup(self, report, restype, resource):
            """测试资源初始化"""
            pass
    
    
    if __name__ == "__main__":
    
        from testbase.resource import LocalResourceManagerBackend
        from testbase.report import HtmlTestReport
    
        dir_report = {}
        TestRunner(
            HtmlTestReport(), resmgr_backend=LocalResourceManagerBackend()
        ).run(SstcTestPlan())
        print(dir_report)
    
    

    拓展 测试报告

    主要引入了一些参数,主要为了以下几点

    • 把测试进度写到redis里
    • 把测试报告返回回去
    点击查看代码
    
    
    import argparse
    
    from testbase.report import ITestResultFactory, report_usage, JSONTestReportBase
    from testbase.testresult import JSONResult
    
    
    class XxxxResult(JSONResult):
        """xxxx test result,先不处理后续有需要再写"""
    
        def get_file(self):
            """
            重写了父类方法,直接让每个测试用例的结果弄到测试报告中
            ,而不是以一个文件的形式单独存在
            # TODO: 目前未遇到内存问题,后续考虑写到redis里
            @return:
            @rtype:
            """
            return self._data
    
    
    class XxxxTestResultFactory(ITestResultFactory):
        """result factory"""
    
        def create(self, testcase):
            return XxxxResult(testcase)
    
    
    class XxxxTestReport(JSONTestReportBase):
        """test report class"""
    
        def __init__(self, dict_report, task_id=None, count_tests=None, redis=None):
            super(JSONTestReportBase, self).__init__()
            self._logs = []
            self._filtered_tests = []
            self._load_errors = []
            self._passed_tests = {}
            self._failed_tests = {}
            self._data = dict_report
            self._data.update(
                {
                    "version": "1.0",
                    "summary": {"tool": "SSTC", "title": "", "environment": {}},
                    "logs": self._logs,
                    "filtered_tests": self._filtered_tests,
                    "load_errors": self._load_errors,
                    "passed_tests": self._passed_tests,
                    "failed_tests": self._failed_tests,
                }
            )
            self._testcase_names = set()
            self._testcase_total_run = 0
            self._task_id = task_id
            self._count_tests = count_tests
            self._redis = redis
    
        def _set_task_progress(self, time_out=3600):
            """
            设置redis测试进度
            key: "tests_task_{}".format(self._task_id)
            value: 百分比   如:100、29
            @return:
            @rtype:
            """
            if self._task_id == None or self._count_tests == None or self._redis == None:
                return
            else:
                if self._count_tests == 0:
                    self._redis.set("tests_task_{}".format(self._task_id), 0)
                else:
                    self._redis.expire("tests_task_{}".format(self._task_id), time_out)
                    self._redis.set(
                        "tests_task_{}".format(self._task_id),
                        int(
                            (len(self._passed_tests) + len(self._failed_tests))
                            * 100
                            / self._count_tests
                        ),
                    )
    
        def log_test_result(self, testcase, testresult):
            self._set_task_progress()
            super(XxxxTestReport, self).log_test_result(testcase, testresult)
    
        def get_testresult_factory(self):
            """获取对应的TestResult工厂
            :returns ITestResultFactory
            """
            return XxxxTestResultFactory()
    
        def end_report(self):
            super(XxxxTestReport, self).end_report()
    
        def begin_report(self):
            super(XxxxTestReport, self).begin_report()
            pass
    
    
    

    使用感受和结论

    • 在qta这套框架在腾讯外用得基本上没有文档,然后官方提供的qq群也不活跃,所有要用的人考虑遇到问题后得自己能看得懂源码(大多数注释都是中文)
    • pyhon3支持得不是很好,所有用python3容易遇到版本兼容问题(相关的依赖包)
    • 如果单纯的只用qtaf,不用其他配套框架qt4i,qt4w这类的会比较容易,不容易出现深坑。
    • 腾讯外没有metis这个这套框架的核心可以用(2021年9月),有条件可以自己去实现。
    • 虽然框架提供了并发执行测试用例,但如果想执行效率更高需要自己定制开发执行器

    结论:

    • 虽然说社区没有其他框架(appium、selenium等)那么活跃,但提供的基础功能很全,且有全中文官方文档对英语不好的人很友好,如果其他选择你都不满意,那么qta全家桶你可以尝试下。
  • 相关阅读:
    ReSharper Tips—GotoImplementation
    Possible multiple enumeration of IEnumerable
    Hello, Razor!
    自话自说——POI使用需要注意一个地方
    css中怎么设置透明度的问题
    记录排查国标直播流播放卡顿的问题
    互联网上做广告的优点
    C#、.Net经典面试题集锦(一)
    什么是MFC
    C/S与B/S 的区别
  • 原文地址:https://www.cnblogs.com/Klay/p/15339223.html
Copyright © 2020-2023  润新知