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 "\n".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两种格式的文件内容。因为这两种都可以,官网示例中只有一个。

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 "\n".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测试文件到此结束。

上一篇:centos 7修改多网卡设备无法连通问题


下一篇:ABB AC 900F学习笔记69-Freelance_Mounting_and_Installation_AC_900F_Controller-29