排除抽象属性在覆盖率报告中的统计

52

我有一个类似于下面这样的抽象基类:

class MyAbstractClass(object):
    __metaclass__ = ABCMeta

    @abstractproperty
    def myproperty(self): pass

当我在我的项目上运行nosetests(使用覆盖率工具),它却抱怨说属性def行未被测试。就我所知,由于实例化抽象类将导致引发异常,因此实际上无法测试该行代码。

是否有任何解决方法,或者我只能接受不到100%的测试覆盖率?

当然,我可以删除ABCMeta用法并仅使基类引发NotImpementedError,但我更喜欢前一种方法。

4个回答

67

对我来说,最好的解决方案是@Wesley在他对接受答案的评论中提到的,具体是用一个文档字符串替换抽象属性中的 'pass',例如:

对我来说,最佳解决方案是@Wesley在他对被接受的回答的评论中提到的,具体地将“pass”替换为抽象属性的文档字符串,例如:

class MyAbstractClass(object):
    __metaclass__ = ABCMeta

    @abstractproperty
    def myproperty(self):
       """ this property is too abstract to understand. """

44

如果您稍作修改,就可以精确地排除抽象属性。让您的抽象属性引发一个错误:

@abstractproperty
def myproperty(self): 
    raise NotImplementedError

您可以指示coverage.py忽略引发NotImplementedError的行。创建.coveragerc文件,并在其中放置以下内容:

[report]
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover

    # Don't complain if tests don't hit defensive assertion code:
    raise NotImplementedError

想了解更多关于你可能希望始终忽略的行的类型的建议,请参见:http://nedbatchelder.com/code/coverage/config.html


1
最终我在IRC上了解到了#pragma: no cover,并采用了内联方式。我不喜欢在抽象属性中使用实现(即使只是raise NotImplementedError),因为这似乎违背了初衷。 - Demian Brecht
5
等等,你同意在每个抽象属性上放置“#pragma: no cover”的注释,但你不同意将其主体从“pass”更改为“raise NotImplementedError”?各有所好,我猜...很高兴你找到了自己喜欢的解决方案。 - Ned Batchelder
1
我完全同意这也不是最优解,但我更喜欢使用内联指令来替代修改抽象基类模块的预期用途和好处。 - Demian Brecht
36
另一种选择是给你的抽象方法或属性添加文档字符串,而不是使用 pass。这样做的额外好处是为你的抽象方法/属性提供文档,说明它们预期如何运作。 - Wesley Baugh
1
@ereOn - 对我来说,docstring解决方案很好用,而且我已经启用了分支覆盖率。如果没有涉及到任何分支,启用分支覆盖率会导致行为发生变化,那么我认为这是一个bug... - ArtOfWarfare
显示剩余2条评论

21

我在我的.coveragerc文件中设置了自定义的跳过逻辑:

[report]
exclude_lines =
    pragma: no cover
    @abstract

通过这种方式,所有的抽象方法和抽象属性都被标记为跳过。


1
能够很好地与tox协同工作! - AugBar
1
有一个小问题... @abstractmethod 可能会有一个子类可以调用的实现。这可能会隐藏应该被覆盖的代码。(请参见 abstractmethod 文档末尾的注释) - Tim Tisdall
1
如果你使用 import abc 而不是 from abc import abstract...,并且由于这些行是正则表达式,你可以使用 @abc\.abstract 来排除所有抽象方法/属性。 - cod3monk3y

2
直接从docs获取。在你的pyproject.toml文件中添加以下部分:
[tool.coverage.report]
exclude_also = [
    "raise AssertionError",
    "raise NotImplementedError",
    "@(abc\\.)?abstractmethod",
    ]

或者到.coveragerc
[report]
exclude_also =
    raise AssertionError
    raise NotImplementedError
    @(abc\.)?abstractmethod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接