美文网首页软件测试
HttpRunner 源码剖析-到底数据驱动是如何实现的?

HttpRunner 源码剖析-到底数据驱动是如何实现的?

作者: Lydia1991 | 来源:发表于2020-06-28 11:30 被阅读0次

    转自:https://testerhome.com/topics/18875

    HttpRunner的执行方式
    hrun /Users/apple/Desktop/hrun_demo/test.yml

    源码

    1. 先分享一个小技巧,一般Pyhton库尤其带有命令行的库,程序的入口都是在根目录的setup.py 文件的 entry_points 中,例如另外一个知名库:分布式异步任务框架Celery,这个技巧有助于以后看知名框架源码知道程序的入口!

    2. 那我们就先看 HttpRunner 的 setup.py 的 entry_points ,找到 101行'hrun=httprunner.cli:main_hrun', 根据包路径可以找到 httprunner/cli.py
      这个模块下有2个命令行函数的实现(重点关注main_hrun),使用的命令解析库是Python标准库argparse,其实建议可以使用click,因为之前在项目中也是使用argparse 后面采用了更方便可读的click

    3. 我们继续看(这里只关心HttpRunner通过YAML 文件路径实现自动化测试的方法).

    def main_hrun():
        ... # 省略
        parser.add_argument(
            'testcase_paths', nargs='*',
            help="testcase file path")
        #①testcase_paths 是一个列表参数.
        ... # 省略
    
    #②找到使用这个参数的地方,搜索下当前文件,找到83行 httprunner/cli.py
        for path in args.testcase_paths:
            runner.run(path, dot_env_path=args.dot_env_path)
    
    #③跳到runner.run 方法,看下264行 httprunner/api.py
    return self.run_path(path_or_tests, dot_env_path, mapping)
    
    # ④跳到self.run_path方法内,看下252行httprunner/api.py
    return self.run_tests(tests_mapping)
    # ⑤这里边有一个tests_mapping的参数,根据变量名可以看到是一个字典,也可以跳转到
    tests_mapping = loader.load_tests(path, dot_env_path) 
    #大概看下具体实现,这个方法的作用就是读取YAML做一些初始化的操作,返回一个dict,你可以理解为返回了所有有关于前置(hooks),测试用例等相关的元信息,我们不再继续深究,回到看到的self.run_tests方法,跳进去看下具体实现。
    
    #⑥看下httprunner/api.py的178行
    self.exception_stage = "add tests to test suite"
    test_suite = self._add_tests(parsed_testcases)
    
    # 其中parsed_testcases是一个list,其实parse_tests方法又把数据做了进一步的处理。
    
    #⑦我们继续跳到self._add_tests 内部看下 39行,这个方法就是实现的核心逻辑了。
    
    # 可以看到这个方法是一个闭包函数,里边内嵌了很多的函数,我们重点看下这个self._add_tests 方法。
    

    因为这个方法非常重要,我们单独拿出来看下(为了方便查看,做了稍微的改变)

    def _add_tests(self, testcases):
    
        def _add_test(test_runner, test_dict):
            def test(self):
                pass
            return test
    
        # 初始化了一个TestSuite的实例,相信经常使用unittest库看到这个就非常熟悉了,主要用于收集测试用例.
        test_suite = unittest.TestSuite()
    
        for testcase in testcases:
            config = testcase.get("config", {})
            test_runner = runner.Runner(config)
    
            # ⑧这一行是这个_add_tests方法的核心,大家平常用Python 知道type可以用来查看对象的类型,
            # 其实type还有另外一个作用,就是用来动态创建类,type是所有类的元类,有兴趣可以看下面 type动态创建类。
    
            TestSequense = type('TestSequense', (unittest.TestCase,), {})
    
            tests = testcase.get("teststeps", [])
            for index, test_dict in enumerate(tests):
                for times_index in range(int(test_dict.get("times", 1))):
                    test_method_name = 'test_{:04}_{:03}'.format(index, times_index)
    
                    # ⑨以下2行也是核心,是为TestSequense类附加了具体的测试方法名和测试方法实现,可以参考下面 type动态创建类。
                    test_method = _add_test(test_runner, test_dict)
                    setattr(TestSequense, test_method_name, test_method)
    
            loaded_testcase = self.test_loader.loadTestsFromTestCase(TestSequense)
            setattr(loaded_testcase, "config", config)
            setattr(loaded_testcase, "teststeps", tests)
            setattr(loaded_testcase, "runner", test_runner)
    
            # ⑩这一行代码就是unittest提供的把测试用例方法添加到用例集,和我们正常使用unittest测试框架写测试类和测试方法是一样的。
            #只不过 HttpRunner使用了很多Python的高级语法,如果你不熟悉的话,可能是很难看明白。
            test_suite.addTest(loaded_testcase)
    
        return test_suite
    
        # 以上⑧⑨步骤总结起来就是:使用type动态创建一个继承与unitest.TestCase的类TestSequense,然后使用setattr方法给这个类附加具体的测试方法test_method,也就是_add_test(test_runner, test_dict)返还的一个函数对象。
    

    type 动态创建类

    In [10]: class Bar:pass
    
    In [11]: type(Bar)
    Out[11]: type
    
    In [12]: Foo = type('Foo', (), {})
    
    In [13]: type(Foo)
    Out[13]: type
    
    # 再联想下我们平时使用unittest写测试类的写法
    In [15]: class TestSequense(unittest.TestCase):
                pass
    
    # 其实是和下面的写法含义相同
    TestSequense = type('TestSequense', (unittest.TestCase,), {})
    
    
    
    # 以下一行代码的作用是为TestSequense设置一个测试方法名test_method_name以及具体实现test_method
    setattr(TestSequense, test_method_name, test_method)
    
    # 例如我们平时使用unittest写的测试用例方法实现
    In [16]: class TestDemo(unittest.TestCase):
                def test_method_name(self):
                    assert 1 ==1
    
    In [17]: TestDemo.__dict__
    Out[17]:
    mappingproxy({'__module__': '__main__',
                  'test_method_name': <function __main__.TestDemo.test_method_name(self)>,
                  '__doc__': None})
    
    而 setattr(TestSequense, test_method_name, test_method)这行代码就是实现了
    'test_method_name': <function __main__.test_method>,
    #这样就动态的创建了测试方法
    
    
    #大概创建的过程:
    In [29]: class TestFoo(unittest.TestCase):
        ...:     def test_methond_name(self):
        ...:         assert 1 == 1
        ...:         print('1 == 1')
        ...:
    
    In [30]: TestFoo1 = type('TestFoo1', (unittest.TestCase,), {})
    
    In [31]: def test_method_name(self):
        ...:         assert 1 == 1
        ...:         print('1 == 1')
        ...:
    
    In [32]: setattr(TestFoo1, 'test_method_name', test_method_name)
    
    In [33]: TestFoo.__dict__
    Out[33]:
    mappingproxy({'__module__': '__main__',
                  'test_methond_name': <function __main__.TestFoo.test_methond_name(self)>,
                  '__doc__': None})
    
    In [34]: TestFoo1.__dict__
    Out[34]:
    mappingproxy({'__module__': '__main__',
                  '__doc__': None,
                  'test_method_name': <function __main__.test_method_name(self)>})
    
    “““
    
    使用type动态创建类需要传以下3个参数:
    ①class的名称-字符串 'TestSequense';
    ②继承的父类集合,Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法(unittest.TestCase, );
    ③class的方法名称与函数绑定。
    我们看下 TestSequense = type('TestSequense', (unittest.TestCase,), {})
    就是123步骤的实现
    ”””
    

    可以参考使用元类


    总结

    以上就是HttpRunner能够直接执行YAML和JSON文件的原理,后续的处理是对unittest的高级封装的使用,例如执行结果的收集TestResult,测试用例的执行unittest.TextTestRunner,收集的结果注入到html静态文件中等等,需要对unittest的源码结构非常的熟悉。

    相关文章

      网友评论

        本文标题:HttpRunner 源码剖析-到底数据驱动是如何实现的?

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