- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我将 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/
我的程序有问题。 我有一个比较两个字符串的条件: (if (eq? (exp1) (exp2))) 当 exp1 给我一个字符串,exp2 给我一个字符串。可以肯定的是,当我更改“eq?”时到“=”,
我们有多种主要使用 GWT 开发的产品,目前由我们的最终客户使用。 想知道 GWT 的路线图。我得到了一些非官方的更新,谷歌正在将 GWT 中开发的产品转移到其他一些新技术。这是真的吗? GWT 的长
我希望每 15 分钟定期构建一次。我在网上看过,我正在使用这个时间表:*/15 * * * * Jenkins 告诉我使用 H/15 * * * * 来平均分配负载而不是 */15 * * * * 有
所以我正试图在 Scheme 中找出整个 call/cc 的东西。下面是我正在使用的代码: (+ 1 (call/cc (lambda (k) (if (number? k)
所以我正试图在 Scheme 中找出整个 call/cc 的东西。下面是我正在使用的代码: (+ 1 (call/cc (lambda (k) (if (number? k)
我们有一个 Azure WebJob,计划在 UTC 每天上午 8:00 运行(CRON - 0 00 08 * * *)。大多数时候它都会正确触发,但有时会触发两次(第二次运行)第一次运行后约 10
我是 Terraform 的新手。我正在尝试通过 azure 管道创建一个简单的存储帐户,但是当我运行我的管道时,我收到错误“太多命令行参数”。我很震惊,我不知道自己做错了什么。有人可以帮忙吗。 这是
我想在某些逻辑中间停止芭蕾舞 Actor 程序。如何使用代码停止 ballerina 中正在运行的程序?我正在寻找相当于 java 中的 System.exit(0) 的东西。 最佳答案 我相信您正在
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 8年前关闭。 Improve this qu
我们有一个 Azure WebJob,计划在 UTC 每天上午 8:00 运行(CRON - 0 00 08 * * *)。大多数时候它都会正确触发,但有时会触发两次(第二次运行)第一次运行后约 10
我是 Terraform 的新手。我正在尝试通过 azure 管道创建一个简单的存储帐户,但是当我运行我的管道时,我收到错误“太多命令行参数”。我很震惊,我不知道自己做错了什么。有人可以帮忙吗。 这是
我正在浏览 htdp 并在一开始的某个地方发现了这个:- Explain why the following sentences are illegal definitions: 1. (define
我正在使用 Laravel 开发成员(member)门户。 成员(member)资格有不同的类别,例如1) 单人2) 成人3) 家庭以及不同价格的所有类型。 我有一个 plans 表和 plans_s
我使用 DreamHost 作为我的网站的服务器,并且我尝试每天、每周和每月执行某个 MySQL 查询来更改我的网站的数据库。我开始在本地主机上使用事件调度程序,然后我发现我无法在 DreamHost
这周我的 crontab 作业发生了一个问题。 设置如下,每两周正常运行一次,直到现在。 10 06 * * 1 test $(($(date +\%W)\%2)) -eq 0 && echo 'te
编写了一个简单的脚本,它将在日志文件中写入日期时间戳,并且每次运行该脚本时,它都会附加到该日志文件中。 #!/bin/sh echo $(date) >> log.txt 当我尝试每 1 分钟安排一次
我对 PIPE 的了解是它用于单向通信,它有助于在两个相关进程之间进行通信。我从一本书中得到了下面的 PIPE 编程代码示例。我正在尝试使用 printf 理解代码并在代码的每一行之后打印出所有点。但
代码如下: (define make-simple-sv-num (lambda (delare) (let ((tal (random-from-to 100000 1000000)))
我目前正在使用“How To Design Programs”——使用 Scheme/Racket;我在 Scheme 的 R5RS 版本中遇到了一个非常奇特的功能。 在进行简单的减法时,尽管使用的是
我想确定时间表的详细信息。例如: 我有一个事件的时间表:event.schedule "Every 3 months on the 10th day of the month" 由哈希表示: {
我是一名优秀的程序员,十分优秀!