天天看点

pytest | 把收集的 yaml 文件转 Item 用例并运行

作者:VT漫步

通过用例收集钩子 pytest_collect_file 把 yaml 文件收集起来的,仅仅只是收集到用例,还不能执行。接下来详细讲解,如何把yaml 文件的内容,转成Item 用例去执行。

pytest_collect_file 收集钩子

准备一个待执行的YAML文件内容test_login.yml

name: login case1
request:
    url: http://127.0.0.1:8000/api/v1/login/
    method: POST
    headers:
        Content-Type: application/json
    json:
        username: test
        password: 123456           

先在conftest.py 写收集钩子

def pytest_collect_file(file_path: Path, parent):
    # 获取文件.yml 文件,匹配规则
    if file_path.suffix == ".yml" and file_path.name.startswith("test"):
        return pytest.File.from_parent(parent, path=file_path)           

如果收集到yaml 文件返回pytest.File.from_parent(parent, path=file_path),在运行的时候会出现报错

============================================ ERRORS ============================================
_____________________________ ERROR collecting case/test_login.yml _____________________________
venv\lib\site-packages\_pytest\runner.py:339: in from_call
    result: Optional[TResult] = func()
venv\lib\site-packages\_pytest\runner.py:370: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
venv\lib\site-packages\_pytest\nodes.py:536: in collect
    raise NotImplementedError("abstract")
E   NotImplementedError: abstract
=================================== short test summary info ====================================
ERROR case/test_login.yml - NotImplementedError: abstract
!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!           

报错位置在nodes.py文件里面的collect() 方法,于是找到nodes.py 里面的Collector

class Collector(Node):
    """Collector instances create children through collect() and thus
    iteratively build a tree."""

    class CollectError(Exception):
        """An error during collection, contains a custom message."""

    def collect(self) -> Iterable[Union["Item", "Collector"]]:
        """Return a list of children (items and collectors) for this
        collection node."""
        raise NotImplementedError("abstract")           

由于collect() 方法是空的,直接raise 一个异常NotImplementedError("abstract"), 于是我们需要重写collect() 方法

YamlFile 重写collect()

对应一个YamlFile 类,继承ytest.File,重写collect()方法

  1. 对应一个YamlFile 类,继承ytest.File,重写collect()方法
  2. name=raw.get(‘name’),name参数是设置用例的名称
  3. values=raw,values是自定义的一个参数,读取的yaml文件测试数据
class YamlFile(pytest.File):

    def collect(self):
        """返回读取内容的Iterable 可迭代对象"""
        raw = yaml.safe_load(self.fspath.open(encoding='utf-8'))
        print(raw)
        # raw 是读取 yml 数据的内容
        yield pytest.Item.from_parent(self, name=raw.get('name'), values=raw)           

再次运行pytest

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <Item login case1>

    def pytest_runtest_call(item: Item) -> None:
        _update_current_test_var(item, "call")
        try:
            del sys.last_type
            del sys.last_value
            del sys.last_traceback
        except AttributeError:
            pass
        try:
>           item.runtest()

venv\lib\site-packages\_pytest\runner.py:167:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <Item login case1>

    def runtest(self) -> None:
        """Run the test case for this item.

        Must be implemented by subclasses.

        .. seealso:: :ref:`non-python tests`
        """
>       raise NotImplementedError("runtest must be implemented by Item subclass")
E       NotImplementedError: runtest must be implemented by Item subclass

venv\lib\site-packages\_pytest\nodes.py:733: NotImplementedError           

这次出现的报错在runner.py 文件,执行runtest() 方法抛出的异常NotImplementedError("runtest must be implemented by Item subclass")

看到这里,说明用例Item 已经生成了,在执行的时候,没有定义一个执行yaml文件的方法,所以报错了

于是找到nodes.py 里面的 Item(Node) 类

class Item(Node):
    """A basic test invocation item.

    Note that for a single function there might be multiple test invocation items.
    """

    def runtest(self) -> None:
        """Run the test case for this item.

        Must be implemented by subclasses.

        .. seealso:: :ref:`non-python tests`
        """
        raise NotImplementedError("runtest must be implemented by Item subclass")           

接下来就需要重写Item 里面的runtest 去执行用例

重写Item 的runtest

最终看到的一个简版执行yaml文件的接口用例conftest.py 如下

import pytest
import requests
import yaml
from pathlib import Path

def pytest_collect_file(file_path: Path, parent):
    # 获取文件.yml 文件,匹配规则
    if file_path.suffix == ".yml" and file_path.name.startswith("test"):
        return YamlFile.from_parent(parent, path=file_path)

class YamlFile(pytest.File):

    def collect(self):
        """返回读取内容的Iterable 可迭代对象"""
        raw = yaml.safe_load(self.fspath.open(encoding='utf-8'))
        print(raw)
        # raw 是读取 yml 数据的内容
        yield YamlTest.from_parent(self, name=raw.get('name'), values=raw)

class YamlTest(pytest.Item):
    def __init__(self, name, parent, values):
        super(YamlTest, self).__init__(name, parent)
        self.name = name
        self.values = values
        self.s = requests.session()

    def runtest(self) -> None:
        """运行用例"""
        request_data = self.values["request"]
        response = self.s.request(**request_data)
        print("\n", response.text)           

继续阅读