how can i test an abstract method in python 2.6

2019-05-13 22:12发布

问题:

I have an abstract class:

import abc


class Hello(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def add(self, foo):
        pass

    @abc.abstractmethod
    def remove(self, foo):
        pass

I'm using abc for do abstract methods, so, when i do:

hello = Hello()

and this error is raised: TypeError: Can't instantiate abstract class Hello with abstract methods add, remove

So I can test this type error with:

self.assertRaises(Exception, Hello)  # but this only test the constructor and i can't get the 100% of code coverage. I need call the add method and the remove method

Extra question: anybody knows how can i assert the message exception in python 2.6? (you can't use the with: for raise assertions.)

How can i test this abstract methods for get the 100% of code coverage?

回答1:

What about creating a doc string of the abstract method instead of using pass as mentioned here, https://stackoverflow.com/a/19275908/469992 ? It can also be used to give some information about what the method is supposed to do in the sub classes.

abstract.py,

from abc import ABCMeta, abstractmethod                                            

class A(object):                                                                   
    __metaclass__ = ABCMeta                                                        

    @abstractmethod                                                                
    def some_method(self):                                                         
        "This method should ..."                                                   

class B(A):                                                                        
    def some_method(self):                                                         
        return 1

test_abstract.py,

import unittest                                                                    
import abstract                                                                    

class TestB(unittest.TestCase):                                                    
    def test(self):                                                                
        self.assertEqual(abstract.B().some_method(), 1)   

Then, using python 2.6.8, nosetests --with-xcoverage outputs,

.
Name       Stmts   Miss  Cover   Missing
----------------------------------------
abstract       7      0   100%   
----------------------------------------------------------------------
Ran 1 test in 0.004s  


回答2:

What about checking the key in class dict:

>>> Hello.__dict__.has_key('__abstractmethods__')
True
>>> Hello.__dict__.has_key('__metaclass__')
True
>>>

You can make sure to overide all the method defined inside Hello.__abstractmethods__ into your subclass.

>>> Hello.__abstractmethods__
frozenset(['add', 'remove'])
>>>

If you missed to re-defined any of those method in your sub-class, you will still getting TypeError for the missed methods:

TypeError: Can't instantiate abstract class Abs_Hello with abstract methods remove

OR how about testing like this :

 def check_all(abstract, sub_class):
    abs_method = abstract.__abstractmethods__
    for m in abs_method:
        if not isinstance(m, sub_class):
            raise TypeError("%s is not defined in subclass %s" % (m, repr(sub_class)))


回答3:

You should never be instantiating an abstract class. The reason you are seeing that error is by design. Abstract methods are implemented in their sub-classes, because that defines the behavior where the classes are different. The abstract class encapsulates behavior that is shared between these sub-classes.

To illustrate, you want something like this:

class Language(Hello):

    def add(self, foo):
        self.baz.append(foo)

    def remove(self, foo):
        self.baz[foo] = None

Notice how Language inherits from Hello. So really you're supposed to test an instance of a sub class of your abstract class, not the abstract class itself.