gpt4 book ai didi

testing - Python 动态测试计划生成

转载 作者:行者123 更新时间:2023-11-28 20:48:56 25 4
gpt4 key购买 nike

我将 Sphinx 用于文档,将 pytest 用于测试。我需要生成一个测试计划,但我真的不想手动生成它。

我突然想到一个巧妙的解决方案是将测试元数据实际嵌入到测试本身中,在它们各自的文档字符串中。此元数据将包括完成百分比、剩余时间等内容。然后我可以运行所有测试(此时主要包括占位符)并从中生成测试计划。这将保证测试计划和测试本身是同步的。

我正在考虑制作一个 pytest 插件或一个 sphinx 插件来处理这个问题。

使用 pytest,我能看到的最接近的钩子(Hook)看起来像 pytest_collection_modifyitems,它在收集所有测试后被调用。

或者,我正在考虑使用 Sphinx 并可能复制/修改 todolist 插件,因为它看起来最接近这个想法。这个输出会更有用,因为输出会很好地插入我现有的基于 Sphinx 的文档,尽管这个插件有很多内容,我真的没有时间花时间去理解它。

文档字符串中可能包含这样的内容:

:plan_complete: 50 #% indicator of how complete this test is
:plan_remaining: 2 #the number of hours estimated to complete this test
:plan_focus: something #what is the test focused on testing

想法是根据函数的名称、文档字符串和嵌入的计划信息生成一个简单的 markdown/rst 或类似的表,并将其用作测试计划。

这样的东西已经存在了吗?

最佳答案

最后,我选择了一个基于 pytest 的插件,因为它的编码简单得多。

如果其他人有兴趣,下面是插件:

"""Module to generate a test plan table based upon metadata extracted from test
docstrings. The test description is extracted from the first sentence or up to
the first blank line. The data which is extracted from the docstrings are of the
format:

:test_remaining: 10 #number of hours remaining for this test to be complete. If
not present, assumed to be 0
:test_complete: #the percentage of the test that is complete. If not
present, assumed to be 100
:test_focus: The item the test is focusing on such as a DLL call.

"""
import pytest
import re
from functools import partial
from operator import itemgetter
from pathlib import Path

whitespace_re = re.compile(r'\s+')
cut_whitespace = partial(whitespace_re.sub, ' ')
plan_re = re.compile(r':plan_(\w+?):')
plan_handlers = {
'remaining': lambda x:int(x.split('#')[0]),
'complete': lambda x:int(x.strip().split('#')[0]),
'focus': lambda x:x.strip().split('#')[0]
}
csv_template = """.. csv-table:: Test Plan
:header: "Name", "Focus", "% Complete", "Hours remaining", "description", "path"
:widths: 20, 20, 10, 10, 60, 100

{tests}

Overall hours remaining: {hours_remaining:.2f}
Overall % complete: {complete:.2f}

"""

class GeneratePlan:
def __init__(self, output_file=Path('test_plan.rst')):
self.output_file = output_file

def pytest_collection_modifyitems(self, session, config, items):
#breakpoint()
items_to_parse = {i.nodeid.split('[')[0]:i for i in self.item_filter(items)}
#parsed = map(parse_item, items_to_parse.items())
parsed = [self.parse_item(n,i) for (n,i) in items_to_parse.items()]

complete, hours_remaining = self.get_summary_data(parsed)

self.output_file.write_text(csv_template.format(
tests = '\n'.join(self.generate_rst_table(parsed)),
complete=complete,
hours_remaining=hours_remaining))

def item_filter(self, items):
return items #override me

def get_summary_data(self, parsed):
completes = [p['complete'] for p in parsed]
overall_complete = sum(completes)/len(completes)
overall_hours_remaining = sum(p['remaining'] for p in parsed)
return overall_complete, overall_hours_remaining


def generate_rst_table(self, items):
"Use CSV type for simplicity"
sorted_items = sorted(items, key=lambda x:x['name'])
quoter = lambda x:'"{}"'.format(x)
getter = itemgetter(*'name focus complete remaining description path'.split())
for item in sorted_items:
yield 3*' ' + ', '.join(map(quoter, getter(item)))

def parse_item(self, path, item):
"Process a pytest provided item"

data = {
'name': item.name.split('[')[0],
'path': path.split('::')[0],
'description': '',
'remaining': 0,
'complete': 100,
'focus': ''
}

doc = item.function.__doc__
if doc:
desc = self.extract_description(doc)
data['description'] = desc
plan_info = self.extract_info(doc)
data.update(plan_info)

return data

def extract_description(self, doc):
first_sentence = doc.split('\n\n')[0].replace('\n',' ')
return cut_whitespace(first_sentence)

def extract_info(self, doc):
plan_info = {}
for sub_str in doc.split('\n\n'):
cleaned = cut_whitespace(sub_str.replace('\n', ' '))
splitted = plan_re.split(cleaned)
if len(splitted) > 1:
i = iter(splitted[1:]) #splitter starts at index 1
while True:
try:
key = next(i)
val = next(i)
except StopIteration:
break
assert key
if key in plan_handlers:
plan_info[key] = plan_handlers[key](val)
return plan_info


在我的 conftest.py 文件中,我在 pytest_addoption 函数 中配置了一个命令行参数:parser.addoption('--generate_test_plan', action ='store_true', default=False, help="生成测试计划")

然后我在此函数中配置插件:

def pytest_configure(config):
output_test_plan_file = Path('docs/source/test_plan.rst')
class CustomPlan(GeneratePlan):
def item_filter(self, items):
return (i for i in items if 'tests/hw_regression_tests' in i.nodeid)

if config.getoption('generate_test_plan'):
config.pluginmanager.register(CustomPlan(output_file=output_test_plan_file))
#config.pluginmanager.register(GeneratePlan())

最后,在我的一个 sphinx 文档源文件中,我只包含输出第一个文件:

Autogenerated test_plan
=======================

The below test_data is extracted from the individual tests in the suite.

.. include:: test_plan.rst


关于testing - Python 动态测试计划生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54807126/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com