python-3.x 查找未标记的pytests测试

e0uiprwp  于 4个月前  发布在  Python
关注(0)|答案(1)|浏览(58)

在我们的项目中,有很多测试需要在一个单独的运行器中完成。所以,我使用pytest.mark来对测试进行分组,并在github操作中基于这些标记构建一个矩阵。问题是有时我们忘记标记测试,它们永远不会运行。有没有办法检测没有标记的测试?也许可以创建一个警告或错误?我想建立一个CI脚本,可以检测到这一点。

j91ykkif

j91ykkif1#

我终于解决了这个问题。

import argparse
import ast
import os
import re
import subprocess
from itertools import groupby

def get_test_lists(*args: str) -> list[str]:
    cmd = [
        "pytest",
        "--collect-only",
        "--disable-warnings",
        "--no-header",
        "--no-summary",
        "-q",
        "--color=no",
        "--capture=no",
        "-s",
        *args,
    ]
    output = subprocess.check_output(cmd, text=True, env=os.environ)
    test_paths_with_params: list[str] = [line for line in output.splitlines(False) if ".py" in line]

    filter_out = map(lambda x: re.sub(r"\[.*?\]", "", x), test_paths_with_params)

    test_paths: list[str] = list(set(filter_out))
    return sorted(test_paths)

def find_tests_without_mark(test_files: list[str]) -> list[str]:
    grouped_tests: dict[str, tuple[str, ...]] = {
        key: tuple(x.split("::", 1)[1] for x in group)
        for key, group in groupby(test_files, lambda x: x.split(".py", 1)[0] + ".py")
    }

    tests_without_mark: list[str] = []

    for file_path, test_functions in grouped_tests.items():
        with open(file_path, "r") as file:
            tree = ast.parse(file.read())
        for node in ast.walk(tree):
            # only for `test_*` function or `Test*` classes
            if (isinstance(node, ast.FunctionDef) and node.name in test_functions) or (
                isinstance(node, ast.ClassDef) and any(test.startswith(node.name) for test in test_functions)
            ):
                has_mark_decorator = False
                for decorator in node.decorator_list:
                    # does it have `pytest.mark.*`
                    has_mark_decorator = (
                        isinstance(decorator, ast.Attribute)
                        and isinstance(decorator.value, ast.Attribute)
                        and isinstance(decorator.value.value, ast.Name)
                        and decorator.value.value.id == "pytest"
                        and decorator.value.attr == "mark"
                    )
                    if has_mark_decorator:
                        break
                if not has_mark_decorator:
                    tests_without_mark.append(f"{file_path}::{node.name}")

    return tests_without_mark

test_files = get_test_lists("./")
tests_without_mark = find_tests_without_mark(test_files)
print(*tests_without_mark, sep="\n")

字符串

相关问题