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?
Unit tests help to ensure functionality and provide a means of verification for refactoring efforts. Code coverage is a measurement of the amount of code that is run by unit tests - either lines, branches, or methods.
Calculating test coverage is actually fairly easy. You can simply take the number of lines that are covered by a test (any kind of test, across your whole testing strategy) and divide by the total number of lines in your application.
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.)
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 pass
es, 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.
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"""
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With