python 覆盖模块可以有条件地忽略单元测试中的行吗?
Can python coverage module conditionally ignore lines in a unit test?
使用 nosetests 和覆盖模块,我想要代码的覆盖报告来反映被测试的版本。考虑这段代码:
import sys
if sys.version_info < (3,3):
print('older version of python')
当我在 python 版本 3.5 中测试时,print()
显示为未测试。我想让覆盖范围忽略该行,但仅当我使用 python 版本 3.3+
进行测试时
有没有办法只在 sys.version_info
不小于 (3,3)
时在 print()
语句中做类似 # pragma: no cover
的事情?实际上,我想做这样的事情:
import sys
if sys.version_info < (3,3):
print('older version of python') # pragma: [py26,py27,py32] no cover
正如您在评论中解释的那样,您担心的是覆盖率报告只会显示行号,您希望避免一次又一次地重新检查这些行号。
另一方面,我不太赞成在代码中加入注释来让一个或另一个工具开心:对我来说,所有这些都会降低可读性。因此,我想提出另一种方法,它可以避免使代码混乱,但仍然可以减轻您一直进行重新检查的负担。
我们的想法是,创建覆盖情况的基线,您可以将未来的覆盖分析结果与之进行比较。例如,来自 coverage.py 的覆盖率报告如下所示(引用自 http://coverage.readthedocs.org/en/coverage-4.0.3/index.html):
Name Stmts Miss Cover Missing
-------------------------------------------------------
my_program.py 20 4 80% 33-35, 39
my_other_module.py 56 6 89% 17-23
-------------------------------------------------------
TOTAL 76 10 87%
此输出可用作 'baseline' 的基础:粗略的想法(改进见下文)是,将此输出存储为 'accepted' 覆盖情况,并对其进行比较针对未来的报道报告。不幸的是,只要行号发生变化,您就会在比较报告时看到差异。为了避免这种情况,可以改进这个基本思路:
在简单脚本的帮助下,您可以转换报告,以便显示相应行的内容而不是行号。例如,基于您上面的代码示例的假设报告可能如下所示:
Name Stmts Miss Cover Missing
-------------------------------------------------------
my_program.py 20 1 5% 3
根据这份报告,您可以为 python 版本 >= 3.3 创建以下 'coverage baseline',例如在文件 coverage-baseline-33andabove.txt
:
中
my_program.py:
- print('older version of python')
例如,即使您在文件顶部添加更多导入行,该基线看起来也是一样的。将为您确定覆盖范围的其他 python 版本创建更多基线文件。
进一步可能的改进可能是分隔行组,例如:
my_program.py:
*
- print('older version of python')
*
- cleanup()
- assert False
my_program2.py:
*
- print('older version of python')
只有当未覆盖的代码发生变化(添加、删除、修改、移动)以及文件名发生变化时,您才会看到差异。然后,差异的出现将需要您存储新的'coverage-baseline',或者添加更多测试,直到再次达到原始基线内容。
另一种选择是对 Python 的不同版本使用不同的 .coveragerc 文件,并为不同的版本设置不同的 exclude_lines
正则表达式。
我看到有些人使用不同的评论字符串,例如 # no cover 3.x
与 # no cover 2.x
。
但请记住,您根本不必使用注释编译指示。正则表达式应用于整行。例如,如果您对条件使用简短的表示法,例如:
if PY2:
blah_py2_stuff_blah()
那么 Python 3 的 .coveragerc 文件可能有:
[report]
exclude_lines =
# pragma: no cover
if PY2:
然后 if PY2:
行将被排除,您无需任何额外的评论或努力。
我为 coverage
库写了一个插件。它可用于根据不同的 user-defined 标准有条件地从覆盖范围中排除块和线。
支持:
sys_version_info
等同于 sys.version_info
os_environ
等同于 os.environ
is_installed
是我们的自定义函数,它试图导入传递的字符串,returns bool value
package_version
是我们的自定义函数,它试图从 pkg_resources 和 returns 其解析版本 获取包版本
这是一个例子:
try: # pragma: has-django
import django
except ImportError: # pragma: has-no-django
django = None
def run_if_django_is_installed():
if django is not None: # pragma: has-django
...
此示例需要添加这些行:
[coverage:run]
# Here we specify plugins for coverage to be used:
plugins =
coverage_conditional_plugin
[coverage:coverage_conditional_plugin]
rules =
"is_installed('django')": has-django
"not is_installed('django')": has-no-django
现在,标有# pragma: has-django
的行在未安装django
时将被忽略,但在安装时被覆盖。反过来将适用于 has-no-django
pragma.
使用 nosetests 和覆盖模块,我想要代码的覆盖报告来反映被测试的版本。考虑这段代码:
import sys
if sys.version_info < (3,3):
print('older version of python')
当我在 python 版本 3.5 中测试时,print()
显示为未测试。我想让覆盖范围忽略该行,但仅当我使用 python 版本 3.3+
有没有办法只在 sys.version_info
不小于 (3,3)
时在 print()
语句中做类似 # pragma: no cover
的事情?实际上,我想做这样的事情:
import sys
if sys.version_info < (3,3):
print('older version of python') # pragma: [py26,py27,py32] no cover
正如您在评论中解释的那样,您担心的是覆盖率报告只会显示行号,您希望避免一次又一次地重新检查这些行号。
另一方面,我不太赞成在代码中加入注释来让一个或另一个工具开心:对我来说,所有这些都会降低可读性。因此,我想提出另一种方法,它可以避免使代码混乱,但仍然可以减轻您一直进行重新检查的负担。
我们的想法是,创建覆盖情况的基线,您可以将未来的覆盖分析结果与之进行比较。例如,来自 coverage.py 的覆盖率报告如下所示(引用自 http://coverage.readthedocs.org/en/coverage-4.0.3/index.html):
Name Stmts Miss Cover Missing
-------------------------------------------------------
my_program.py 20 4 80% 33-35, 39
my_other_module.py 56 6 89% 17-23
-------------------------------------------------------
TOTAL 76 10 87%
此输出可用作 'baseline' 的基础:粗略的想法(改进见下文)是,将此输出存储为 'accepted' 覆盖情况,并对其进行比较针对未来的报道报告。不幸的是,只要行号发生变化,您就会在比较报告时看到差异。为了避免这种情况,可以改进这个基本思路:
在简单脚本的帮助下,您可以转换报告,以便显示相应行的内容而不是行号。例如,基于您上面的代码示例的假设报告可能如下所示:
Name Stmts Miss Cover Missing
-------------------------------------------------------
my_program.py 20 1 5% 3
根据这份报告,您可以为 python 版本 >= 3.3 创建以下 'coverage baseline',例如在文件 coverage-baseline-33andabove.txt
:
my_program.py:
- print('older version of python')
例如,即使您在文件顶部添加更多导入行,该基线看起来也是一样的。将为您确定覆盖范围的其他 python 版本创建更多基线文件。
进一步可能的改进可能是分隔行组,例如:
my_program.py:
*
- print('older version of python')
*
- cleanup()
- assert False
my_program2.py:
*
- print('older version of python')
只有当未覆盖的代码发生变化(添加、删除、修改、移动)以及文件名发生变化时,您才会看到差异。然后,差异的出现将需要您存储新的'coverage-baseline',或者添加更多测试,直到再次达到原始基线内容。
另一种选择是对 Python 的不同版本使用不同的 .coveragerc 文件,并为不同的版本设置不同的 exclude_lines
正则表达式。
我看到有些人使用不同的评论字符串,例如 # no cover 3.x
与 # no cover 2.x
。
但请记住,您根本不必使用注释编译指示。正则表达式应用于整行。例如,如果您对条件使用简短的表示法,例如:
if PY2:
blah_py2_stuff_blah()
那么 Python 3 的 .coveragerc 文件可能有:
[report]
exclude_lines =
# pragma: no cover
if PY2:
然后 if PY2:
行将被排除,您无需任何额外的评论或努力。
我为 coverage
库写了一个插件。它可用于根据不同的 user-defined 标准有条件地从覆盖范围中排除块和线。
支持:
sys_version_info
等同于sys.version_info
os_environ
等同于os.environ
is_installed
是我们的自定义函数,它试图导入传递的字符串,returns bool valuepackage_version
是我们的自定义函数,它试图从 pkg_resources 和 returns 其解析版本 获取包版本
这是一个例子:
try: # pragma: has-django
import django
except ImportError: # pragma: has-no-django
django = None
def run_if_django_is_installed():
if django is not None: # pragma: has-django
...
此示例需要添加这些行:
[coverage:run]
# Here we specify plugins for coverage to be used:
plugins =
coverage_conditional_plugin
[coverage:coverage_conditional_plugin]
rules =
"is_installed('django')": has-django
"not is_installed('django')": has-no-django
现在,标有# pragma: has-django
的行在未安装django
时将被忽略,但在安装时被覆盖。反过来将适用于 has-no-django
pragma.