Consider this example:
class A:
def do_stuff(self):
# ...
some_var = self.helper_method()
# ...
def helper_method(self):
# This method must be implemented by subclass
raise NotImplementedError()
class B(A):
def helper_method(self):
# implementation for class B
class C(A):
def helper_method(self):
# implementation for class C
My task is to write unit tests for A, B and C classes (especially for do_stuff).
But how can I test A class if I cannot use some of it methods directly?
Should I just test only B and C classes (which have implementation for helper_method)
or maybe there is common way for testing abstract classes in Python?
You don't really have an abstract base class, at least as far as the language is concerned. Nothing is stopping you from instantiating it.
a = A()
If you were using the abc module to define classes that you can't instantiate:
class A(metaclass=abc.ABCMeta):
...
then you can make A instantiable by overriding its set of abstract methods:
A.__abstractmethods__ = frozenset()
a = A()
# test away
In either case, you can still test that an abstract method either raises NotImplementedError
try:
a.helper_method()
except NotImplementedError:
print("Test passed")
else:
print("Test failed")
or test its default implementation as needed.
do_stuff exists on A, so test it on A. The helper methods exist on the concrete classes so test them there. You can use the unittest.mock module to temporarily patch the abstract class so it will work with your test, and also patching the abstract method to return a specific value -- so that its logic is not under test. Given all that, this is how I would test an abstract class.
Given some abstract class:
from abc import abstractmethod, ABC
class MyAbstract(ABC):
def concrete_method(self):
i = self.abstract_method()
return i ** 2
#abstractmethod
def abstract_method(self):
"""return some int"""
pass
This is how I would test it.
from unittest import main, TestCase
from unittest.mock import patch, Mock
from module_under_test import MyAbstract
class TestMyAbstract(TestCase):
def test_cannot_instantiate(self):
"""showing we normally can't instantiate an abstract class"""
with self.assertRaises(TypeError):
MyAbstract()
#patch.multiple(MyAbstract,
__abstractmethods__=set(),
abstract_method=Mock(return_value=3))
def test_concrete_method(self):
"""patch abstract class and its abstract methods for duration of the test"""
# given
my_abstract = MyAbstract()
expected = 9
# when
actual = my_abstract.concrete_method()
# then
self.assertEqual(actual, expected)
if __name__ == "__main__":
main()
You should test logic, not the implementation. A's do_stuff() method has no logic itself, right? What it does depends on whether you are dealing with B or C. Instead it seems to me that it would make more sense to test B's and C's do_stuff() methods - you know what exactly they should do.
As #chepner has already answered your question, not to digress, but you should try to avoid using abstract classes in Python. Abstract classes don't or rather shouldn't serve much purpose much purpose in Dynamic DuckTyped languages like Python, Ruby etc. In Duck-typing as long as particular instance responds to a particular behavior, one shouldn't enforce it to be a child of a particular abstract class.
Related
Using abc, I can create abstract classes using the following:
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def foo(self):
print('foo')
class B(A):
pass
obj = B()
This will fail because B has not defined the method foo.
This mimics the abstract method functionality in Java.
I wanted to know if the abstract class functionality is also present in Python, where instantiation of a class is prevented without having any abstract methods.
The conventional way to create an abstract class in Python is to raise the built-in exception NotImplementedError.
class A(object):
def __init__(self):
raise NotImplementedError('abstract base class')
class B(A):
def __init__(self):
# don't call A.__init__ here.
pass
b = B()
# a = A() # This will fail.
Yes. You can.
If you want to not enforce method implementation:
Simply inherit from ABC but don't delcare a method abstract, so it needn't be implemented in its subclasses.
If you want the abstract class to enforce the implementation of all methods:
Decorate all methods.
If you want to enforce the implementation of a method that does not belong to an ABC:
Raise NotImplementedErrorin the method. This won't prevent instantiation, but usage. However, if you want to prevent it in the instantiation, you should rather use ABC's.
You can also delcare __init__ an abstractmethod, but generally this does not look very useful to me.
I need to use unittest in python to write some tests. I am testing the behavior of 2 classes, A and B, that have a lot of overlap in behavior because they are both subclasses of C, which is abstract. I would really like to be able to write 3 testing classes: ATestCase, BTestCase, and AbstractTestCase, where AbstractTestCase defines the common setup logic for ATestCase and BTestCase, but does not itself run any tests. ATestCase and BTestCase would be subclasses of AbstractTestCase and would define behavior/input data specific to A and B.
Is there a way to create an abstract class via python unittest that can take care of setup functionality by inheriting from TestCase, but not actually run any tests?
Sure, construct like that will surely work:
class BaseTestCase(unittest.TestCase):
def setUp(self):
pass # common teardown
def tearDown(self):
pass # common teardown
class ATestCase(BaseTestCase):
def test1(self):
pass
class BTestCase(BaseTestCase):
def test1(self):
pass
If knowledge from ATestCase or BTestCase is required in BaseTestCase simply override some method in subclasses but use it in superclass.
class BaseTestCase(unittest.TestCase):
def setUp(self):
self.instance = self._create_instance()
def _create_instance(self):
raise NotImplementedError()
class ATestCase(BaseTestCase):
def _create_instance(self):
return A()
class BestCase(BaseTestCase):
def _create_instance(self):
return B()
Note that if any test_(self) methods will be implemented in BaseTestCase, they'll run (and fail due to failing setUp) when discovered by automated runners.
As a workaround you may use skipTest in your setUp clause in abstract test and override it in subclasses.
class BaseTestCase(unittest.TestCase):
def setUp(self):
self.instance = self._create_instance()
def _create_instance(self):
self.skipTest("Abstract")
def test_fromBase(self):
self.assertTrue(True)
Note that skipping test_fromBase (e.g. via decorator) won't be good, since 'test should be skipped' logic will be inherited by all subclasses.
I tried Łukasz’s answer and it works, but I don’t like OK (SKIP=<number>) messages. For my own desires and aims for having a test suite I don’t want me or someone to start trusting any particular number of skipped tests, or not trusting and digging into the test suite and asking why something was skipped, and always?, and on purpose? For me that’s a non-starter.
I happen to use nosetests exclusively, and by convention test classes starting with _ are not run, so naming my base class _TestBaseClass is sufficient.
I tried this in Pycharm with Unittests and py.test and both of those tried to run my base class and its tests resulting in errors because there’s no instance data in the abstract base class. Maybe someone with specific knowledge of either of those runners could make a suite, or something, that bypasses the base class.
I am simply not experienced enough in Python OO programming to know how this is done: If I have several classes that are subclasses of a unittest.TestCase subclass. How should the superclass' methods reference variables of the subclasses when the latter call these methods? Let me try to illustrate it with this, probably wrong, example:
import unittest
class TestSuper(unittest.TestCase):
def test_method(self):
# do something, e.g.
pass
class TestSub1(TestSuper):
def setUp(self):
self.some_parameter = 1
class TestSub2(TestSuper):
def setUp(self):
self.some_parameter = 2
if __name__ == '__main__':
unittest.main()
Now, I cannot figure out how to correcty reference TestSub1.parameter or TestSub2.parameter, respectively, when TestSuper.test_method is called from the subclasses.
I am inspired by https://stackoverflow.com/a/25695512/865169, but here I am trying achieve having multiple test cases that do the same but only differ in their set-up. I can of course achieve all this by just copy-pasting my test case definitions, but I find that bad coding practice.
Just use self.some_parameter; self is always bound to the right instance here.
However, take into account that unittest.main() will run test_method on *all three test cases (the baseclass included), so you'll have to provide a default value for some_parameter on the base class.
If you don't want TestSuper to be seen as a test case, don't inherit from unittest.TestCase. Use it as a mix-in instead:
import unittest
class TestMixin(object):
def test_method(self):
# do something, e.g.
pass
class TestSub1(unittest.TestCase, TestMixin):
def setUp(self):
self.some_parameter = 1
class TestSub2(unittest.TestCase, TestMixin):
def setUp(self):
self.some_parameter = 2
if __name__ == '__main__':
unittest.main()
Now unittest.main() will only find two test cases (TestSub1 and TestSub2) and run test_method only on those cases.
In Java, for example, you can make a class MyClass with certain methods that are specified but not implemented in MyClass, but must be implemented in any class MySubClass that inherits from MyClass. So basically there is some common functionality among all subclasses you want, so you put it in MyClass, and there is some functionality unique (but required) for each subclass, so you want it in each subclass. How can this behavior be achieved in Python?
(I know there are concise terms to describe what I'm asking, so feel free to let me know what these are and how I can better describe my question.)
A very basic example but the abc docs provide a few more
import abc
class Foo():
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def bar(self):
raise NotImplemented
class FooBar(Foo):
pass
f = FooBar()
TypeError: Can't instantiate abstract class FooBar with abstract methods bar
You can't require the implementation of a method in a subclass in a way that will break at compile-time, but the convention on writing a method on the base class that must be implemented in the subclasses is to raise NotImplementedError.
Something like this:
class MyBase(object):
def my_method(self, *args, **kwargs):
raise NotImplementedError("You should implement this method on a subclass of MyBase")
Then your subclasses can implement my_method, but this will break only when the method is called. If you have comprehensive unit tests, as you should, this won't be a problem.
Suppose that I have the following python base class:
class BaseClass(object):
def a():
"""This method uses method b(), defined in the inheriting class"""
And also a class that inherites BaseClass:
class UsedByUser(BaseClass):
def b():
"""b() is defined here, yet is used by the base class"""
My user would only create instances of class UsedByUser. Typical use would be:
if __name__ == '__main__':
# initialize the class used by the user
usedByUser = UsedByUser()
# invoke method a()
usedByUser.a()
My questions is, is the above use problematic? is this a valid approach, or must I also define method b() in BaseClass and then override it in UsedByUser?
I would define the b method in the BaseClass too:
class BaseClass(object):
def b(self):
raise NotImplementedError('b must be implemented by a subclass')
Remember: explicit is better than implicit and given that the method a needs the method b anyways, better raise a meaningful exception rather than a general AttributeError.
It is worth to point out that this is absolutely NOT needed from a syntactic point of view, but it adds clarity to the code and enforces the subclass to provide an implementation.
The use is correct. Classes can define methods that can be overriden
from subclasses, but those can also define new methods. Defining
every method needed for subclasses in the superclass seems a bit
senseless. (Since object then would also need to have every
function defined ?)
A subclass often has a different bahviour to another subclass.
class Vehicle(object):
def Refuel(self):
# ...
class Plane(Vehicle):
def Fly(self):
# ...
class Car(Vehicle):
def Drive(self):
# ...
Edit: I misread the code.
If only you create a subclass of it and make sure subclasses have B(), then it's theoratically ok, but bad style. It makes more sense and is safer to give the superclass attributes and methods that are used by the superclass. -> Define B()
Sounds like you want A to call a protected member function of UsedByUser that can't go in the abstract BaseClass). Try prefixing the protected function with an underscore (although note this is just a convention used by Python and not strictly checked, as mentioned here).
class BaseClass(object):
def A(self):
print "Grettings from A"
self._B()
def _B(self):
raise NotImplementedError('b must be implemented by a subclass')
class UsedByUser(BaseClass):
def _B(self):
""" prefix with underscore is a python convention for a protected member function """
print "B rocks!"
if ( __name__=='__main__' ):
usedByUser = UsedByUser()
usedByUser.A()
Find more on this convention in the PEP guidelines.
Edit:
As GaretJax suggested, I added a BaseClass _B method for clarity. Nice tip!
BaseClass can't assume that a UsedByUser object exists, so it can't use a B() method from it. You probably want to define a B() in BaseClass, that either does nothing (if it's sensible to attempt B() with something that doesn't support it) or raises an exception (if it's not sensible to attempt B()).
If you tell us what A and B are in your use case, we may be able to advise you better.