• pytest+yaml+allure接口自动化测试框架03.读取测试用例文件


    前言

    ​ 使用yaml作为测试用例,我们就需要对文件的内容进行读取,常规来说的应该是通过pyyaml对读取到的内容进行数据解析,然后使用pytest parametrize参数化功能进行数据参数化用例测试。但是完事之后,这样的方式好像不是很优雅,写的代码组织起来比较费劲,于是乎,我在pytest的官方文档中,发现了一套更为一套非常优雅的测试执行方式,他们称之为non-python test的测试模式。

    具体内容可以查看官方文档,感兴趣的可以去看看:Working with non-python tests — pytest documentation

    # content of conftest.py
    import pytest
    
    
    def pytest_collect_file(parent, path):
        if path.ext == ".yaml" and path.basename.startswith("test"):
            return YamlFile.from_parent(parent, fspath=path)
    
    
    class YamlFile(pytest.File):
        def collect(self):
            # We need a yaml parser, e.g. PyYAML.
            import yaml
    
            raw = yaml.safe_load(self.fspath.open())
            for name, spec in sorted(raw.items()):
                yield YamlItem.from_parent(self, name=name, spec=spec)
    
    
    class YamlItem(pytest.Item):
        def __init__(self, name, parent, spec):
            super().__init__(name, parent)
            self.spec = spec
    
        def runtest(self):
            for name, value in sorted(self.spec.items()):
                # Some custom test execution (dumb example follows).
                if name != value:
                    raise YamlException(self, name, value)
    
        def repr_failure(self, excinfo):
            """Called when self.runtest() raises an exception."""
            if isinstance(excinfo.value, YamlException):
                return "
    ".join(
                    [
                        "usecase execution failed",
                        "   spec failed: {1!r}: {2!r}".format(*excinfo.value.args),
                        "   no further details known at this point.",
                    ]
                )
    
        def reportinfo(self):
            return self.fspath, 0, f"usecase: {self.name}"
    
    
    class YamlException(Exception):
        """Custom exception for error reporting."""
    

    可以看到官方文档中以极其优雅的方式通过yaml文件驱动了两个测试用例。我们也将在此基础上进行扩展衍生。

    读取yaml文件

    ​ 我们根据官方文档中的示例文件,在这个基础上进行修改,加入我们的内容。

    pytest_collect_file

    首先我们修改pytest_collect_file函数中的内容,让他支持yamlyml两种格式的文件内容。因为这两种都可以,官网示例中只有一个。

    if path.ext in (".yaml", ".yml") and path.basename.startswith("test"):
        return YamlFile.from_parent(parent, fspath=path)
    

    YamlFile.collect

    接下来修改我们的YamlFile.collect方法,这里面就是对读出来的详细内容按照设置的格式进行处理,该存入缓存的放入缓存,该执行测试的时候执行测试。

    if not any(k.startswith('test') for k in raw.keys()):
        raise YamlException("{}yaml non test found".format(self.fspath))
    

    通过这个语句我们先判断一下,有没有测试用例,如果没有测试用例我们直接就报错了,不在执行,抛出异常,这个异常需要我们自己封装一下。我们打开common/exceptions.py文件。输入以下内容:

    # -*- coding: utf-8 -*-
    __author__ = 'wxhou'
    __email__ = '1084502012@qq.com'
    """
    异常类
    """
    from requests.exceptions import RequestException
    
    
    class YamlException(Exception):
        """Custom exception for error reporting."""
    
        def __init__(self, value):
            self.value = value
    
        def __str__(self):
            return "
    ".join(
                [
                    "usecase execution failed",
                    "   spec failed: {}".format(self.value),
                    "   For more details, see this the document.",
                ]
            )
    

    这个就是当我们发现yaml文件中没有符合的测试标签内容后抛出的异常类。

    然后我们接着先读取全局变量:

    if variable := raw.get('variable'):
        for k, v in variable.items():
            cache.set(k, v)
    

    我们把yaml文件中预设的全局变量信息中全部存在我们设置的缓存模块中,这样在测试过程中我们可以随时的去用。

    继续读取配置文件。

    if config := raw.get('config'):   
        for k, v in config.items():
            cache.set(k, v)
    

    然后我们读取常用的测试信息也放入缓存之中,方便运行过程中随时去调用。

    最后我们来处理一下。测试用例部分:

    if tests := raw.get('tests'):
        for name, spec in tests.items():
            yield YamlTest.from_parent(self,
                                       name=spec.get('description') or name,
                                       spec=spec)
    

    可以看到,在官方文档中使用了sorted函数进行了再次排序。我这里没有是因为再次排序会破坏用例的结构和顺序。最后输出的时候spec.get('description') or name的写法先获取yaml文件中我们设置的中文标识,如果中文标识不存在则继续使用英文标识。其余和官方文档保持一致。

    以上就是做出的改动,我们来看看吧:

    import yaml
    import pytest
    from common.cache import cache
    from common.exceptions import YamlException
    
    
    def pytest_collect_file(parent, path):
        if path.ext in (".yaml", ".yml") and path.basename.startswith("test"):
            return YamlFile.from_parent(parent, fspath=path)
    
    
    class YamlFile(pytest.File):
    
        def collect(self):
            raw = yaml.safe_load(self.fspath.open(encoding='utf-8'))
            if not any(k.startswith('test') for k in raw.keys()):
                raise YamlException("{}yaml non test found".format(self.fspath))
            if variable := raw.get('variable'):
                for k, v in variable.items():
                    cache.set(k, v)
            if config := raw.get('config'):
                for k, v in config.items():
                    cache.set(k, v)
            if tests := raw.get('tests'):
                for name, spec in tests.items():
                    yield YamlTest.from_parent(self,
                                               name=spec.get(
                                                   'description') or name,
                                               spec=spec)
    

    站在巨人的肩膀上才能看得更远。在pytest non-python tests的内容之上做了一些改动,使得读取文件更加贴合我们定义的yaml文件内容。在精简了很多代码的同时我们也达到了预期的效果。

    至此,本章的读取yaml测试文件到此结束。

    随风挥手 ——土木狗的IT生涯 群: 299524235
  • 相关阅读:
    Ios国际化翻译工具
    软件是什么
    angular2实现图片轮播
    DIV+CSS布局最基本的内容
    angular2中使用jQuery
    如何在Ionic2项目中使用第三方JavaScript库
    Ionic2项目中使用Firebase 3
    Ionic2中ion-tabs输入属性
    The Router路由
    templating(模板)
  • 原文地址:https://www.cnblogs.com/wxhou/p/InterfaceYamlFramework03.html
Copyright © 2020-2023  润新知