Python接口模式和单元测试代码覆盖率

9

我正在使用unittest进行测试,同时使用coverage进行代码覆盖率检测。

我广泛使用接口模式,并注意到整体代码覆盖率百分比受“未经测试的接口”影响较大。

请考虑以下内容:

class IReader(object):
    @abstractmethod
    def read(self):
        pass


class Reader(IReader):
    def read(self):
        # whatever

我测试了Reader,但(显然)我没有测试IReader,因此pass指令被标记为未经测试。

是否有一种方法可以忽略coverage中的接口?
由于这是我最早的Python项目之一,我做得完全错了吗?


2
在代码中添加 #pragma: no cover 注释。或者也可以使用 raise NotImplementedError(尽管我不确定它是否会默认从覆盖范围中排除)。 - Łukasz Rogalski
@ŁukaszRogalski 我认为它不会,但你可以配置覆盖率来实现。请参阅文档和我的回答。 - Jérôme
1个回答

10

我其实不太明白为什么要定义这个read方法,然后使用单个指令pass。如果你不需要它,就让它抛出NotImplementedError异常。

class IReader(object):
    @abstractmethod
    def read(self):
        raise NotImplementedError

文档中所指定的,抽象方法可以有一个由子类使用super()的实现。但在你的情况下,它什么也不做。因此,除非你有充分的理由,否则最好让它引发NotImplementedError

(可以说,一个好的理由可能是所有子类都因某种原因调用super().my_method(),因此您需要在抽象类中实现所有方法。)

如何从覆盖率报告中排除抽象方法

无论如何,测试覆盖率只是您构建的指示器:您要测试的代码部分和您实际测试的部分。定义“要测试的代码”取决于您。

如果您认为有兴趣,可以添加一个测试来检查抽象方法是否返回NotImplementedError或者只是pass

或者你可能认为(这似乎是合理的)测试这个方法是没有意义的,那么在测试覆盖报告中排除该方法的方式是使用#pragma: no coverexcluding
class IReader(object):  #pragma: no cover
    @abstractmethod
    def read(self):
        pass

上面链接的文档页面展示了如何通过在您的配置文件中添加以下内容来排除所有NotImplementedError方法:
[report]
exclude_lines =
    pragma: no cover
    raise NotImplementedError

为了避免在每个抽象方法中添加pragma,您可以这样做。

2020编辑

我刚刚注意到@abstractmethod装饰器。由于它阻止类被实例化,所以我不会引发NotImplementedError。请参见this other answer.

我只会保留该方法为空。从语法上讲,文档字符串就足够了。

class IReader(object):
    @abstractmethod
    def read(self):
        """Read data"""

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