9

I'm working in a python project using unittest for testing along with coverage for code coverage.
I use the interface pattern extensively and I noticed that the overall code coverage percentage is heavily influenced by "non tested interfaces".
Consider the following:

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


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

I test Reader but (obviously) I do not test IReader so the pass instruction is marked as not covered by tests.

Is there a way to ignore the interfaces from coverage?
Since this is one of my first python project, am I doing this totally wrong?

Stefano Azzalini
  • 1,027
  • 12
  • 21
  • 2
    Add `#pragma: no cover` comment inline. Or maybe `raise NotImplementedError` (although I'm not sure if it'll be excluded by default from coverage). – Łukasz Rogalski Feb 13 '17 at 08:39
  • @ŁukaszRogalski I don't think it does but you can configure coverage to do so. See [the docs](http://coverage.readthedocs.io/en/coverage-4.3.4/excluding.html#advanced-exclusion) and my answer. – Jérôme Feb 13 '17 at 13:14

1 Answers1

10

I don't really see the point of having this read method defined with pass as single instruction. If you don't need it, let it raise NotImplementedError.

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

As specified in the docs, an abstract method can have an implementation used by children with super(). But in your case, it does nothing. Therefore, unless you have a good reason, you might as well let it raise NotImplementedError.

(Arguably, a good reason could be a pattern where all your children call super().my_method() for some reason, so you need an implementation for all methods in the abstract class.)

 How to exclude the abstract method from coverage report 

Regardless, the test coverage is just an indicator you build: the part of the code you want to test that you actually test. Defining the "code you want to test" is up to you.

You could add a test to check that the abstract method returns NotImplementedError or just passes, if you see an interest in this.

Or you may think (which seems reasonable) that testing this is pointless, in which case excluding the method from coverage report #pragma: no cover seems like the way to go:

class IReader(object):  #pragma: no cover
    @abstractmethod
    def read(self):
        pass

The doc page linked above shows how you can also exclude all NotImplementedError methods adding this to your configuration file:

[report]
exclude_lines =
    pragma: no cover
    raise NotImplementedError

so that you don't have to add a pragma to each abstract method.


2020 edit

I just noticed the @abstractmethod decorator. Since it prevents the class from being instantiated, I wouldn't raise NotImplementedError. See this other answer.

I'd just leave the method empty. Syntaxically, a docstring is enough

class IReader(object):
    @abstractmethod
    def read(self):
        """Read data"""
Jérôme
  • 13,328
  • 7
  • 56
  • 106