I have an abstract class that I am using with the template pattern and some children that implement specific methods.
class TemplateClass(ABC):
#abstractmethod
def special_process_1():
pass
def common_process():
do_something()
def common_filter():
filter_something()
def __call__():
self.common_filter()
self.special_process_1()
self.common_process()
class classA(TemplateClass):
def special_process_1():
something_A_needs()
class classB(TemplateClass):
def special_process_1():
something_B_needs()
Now, I would like to test the __call__ method, but I am not sure what would be the best way. I think the best would be if I could test on the template so that I don't need to replicate test for classA and classB. However, I am not sure how to do it.
I have tried to test the template as follows:
#fixture
def template_mock():
with patch("TemplateClass.__abstractmethods__", set()):
t = TemplateClass()
t.special_process_1 = MagicMock(return_value=False)
yield t
The problem with the above is that on the tests, mypy would complain about template_mock.special_process_1 being a callable instead of a mock, so it does not have any return_value attribute.
Would be open to what other alternatives are there or if it makes sense at all to be testing this on the base class
Related
Let's say I have a class and would like to implement a method which creates an instance of that class. What I have is 2 options:
static method,
class method.
An example:
class DummyClass:
def __init__(self, json):
self.dict = json
#staticmethod
def from_json_static(json):
return DummyClass(json)
#classmethod
def from_json_class(cls, json):
return cls(json)
Both of the methods work:
dummy_dict = {"dummy_var": 124}
dummy_instance = DummyClass({"test": "abc"})
dummy_instance_from_static = dummy_instance.from_json_static(dummy_dict)
print(dummy_instance_from_static.dict)
> {'dummy_var': 124}
dummy_instance_from_class = DummyClass.from_json_class(dummy_dict)
print(dummy_instance_from_class.dict)
> {'dummy_var': 124}
What I often see in codes of other people is the classmethod design instead of staticmethod. Why is this the case?
Or, rephrasing the question to possibly get a more comprehensive answer: what are the pros and cons of creating a class instance via classmethod vs staticmethod in Python?
Two big advantages of the #classmethod approach:
First, you don't hard-code the name. Given modern refactoring tools in IDEs, this isn't as big of a deal, but it is nice to not have your code break if you change the name of your Foo, class to Bar::
class Bar:
#statmicmethod
def make_me():
return Foo()
Another advantage (at least, you should understand the difference!) is how this behaves with inheritance:
class Foo:
#classmethod
def make_me_cm(cls):
return cls()
#staticmethod
def make_me_sm():
return Foo()
class Bar(Foo):
pass
print(Bar.make_me_cm()) # it's a Bar instance
print(Bar.make_me_sm()) # it's a Foo instance
I have classes like these
class Test:
def __str__(self):
return "Test"
class Test1(Test):
def __str__(self):
return "Test1"
class Test2(Test):
def __str__(self):
return "Test2"
class Runner:
pass
class Runner1(Runner):
def run(self):
print("I'm a method, doing this and that")
print(f"And I use {Test1()}")
class Runner2(Runner):
def func2(self):
print("I'm a method, doing this and that")
test = Test2()
print(f"And I use {test}")
and I would like to discover all Runner classes, which use Test instances, like this:
for klass, func, ref in get_all_references(Runner):
if isinstance(ref, Test):
print(f"{klass.__name}.{func.__name} uses Test!")
That is, I'm looking for the get_all_references method, which returns all referenced objects of any classes of type Runner (and their methods), which I can inspect for class type/inheritance.
The motivation behind this is to discover all places (class/method names) where instances of Test are used.
I think part of the problem is solved by static analyzers/doc creators/cross reference builders, but I couldn't find any which could be used to get this information via an API.
i think gc module has several useful functions in that matter, but it sounds like gc.get_referrers() is what you need.
I'm currently refactoring features and I ended up with this abstractions
I have this classes
class AbstractClassA(SomeOtherAbstractClass):
#abstractmethod
def some_abstract_method(self):
pass
def my_method(self)):
service.some_method
class AbstractClassB(AbstractClassA):
#abstractmethod
def another_abstract_method(self):
pass
def some_abstract_method(self):
some_implementation
def my_method(self):
super().my_method()
do_any_other_stuff
And I need to test if the AbstractClassB.my_method calls super().my_method().
I've tried to test this by creating some ImplementationClass that inherits from AbstractClassB and then mocking the AbstractClassA.my_method and checking if it was called but it didn't work...
class AbstractClassBImplementation(AbstractClassB):
def some_abstract_method(self):
calls_service()
class TestAbstractClassB(TestCase):
#patch('module.submodule.AbstractClassA.my_method')
def test_class_b_calls_class_a_my_method(self, my_method_mock):
instance = AbstractClassBImplementation()
instance.my_method()
self.assertTrue(my_method_mock.called)
Someone know how to test this?
I have an abstract class that has multiple #abstractmethod that raises NotImplementedError("Need to implement this").
How do I set up a test-case using python's builtin unittest?
I tried using #patch.multibyte but it is not working.
I dunno what you are trying to achieve by using #patch.multibyte, but if your goal is to test that you have to implement abstract methods in your concrete classes it's just a matter of using assertRaises.
Let's pretend to have an abstract class MyAbstractClass in module my_api.py:
import abc
class MyAbstractClass(abc.ABC):
#abc.abstractmethod
def method_1(self):
pass
Then you write the tests in my_api_tests.py:
from unittest import TestCase
from my_api import MyAbstractClass
class MyConcreteClassWithoutImplementations(MyAbstractClass):
pass
class MyConcreteClassWithImplementations(MyAbstractClass):
def method_1(self):
return 1
class MyAbstractClassTest(TestCase):
def test_cannot_instantiate_concrete_classes_if_abstract_method_are_not_implemented(self):
self.assertRaises(TypeError, lambda: MyConcreteClassWithoutImplementations())
def test_can_instantiate_concrete_classes_if_abstract_method_are_implemented(self):
error = None
try:
my_object = MyConcreteClassWithImplementations()
self.assertEqual(my_object.method_1(), 1)
except TypeError as e:
error = e
self.assertIsNone(error)
...but you are actually testing Python's API, not your own code, so such tests are not useful... you have to test your own business logic ;)
The gist of the question: if inheriting multiple classes how can I guarantee that if one class is inherited, a compliment Abstract Base Class (abc) is also used by the child object.
I've been messing around with pythons inheritance trying to see what kind of cool stuff I can do and I came up with this pattern, which is kind of interesting.
I've been trying to use this make implementing and testing objects that interface with my cache easier. I've got three modules:
ICachable.py
Cacheable.py
SomeClass.py
ICacheable.py
import abc
class ICacheable(abc.ABC):
#property
#abc.abstractmethod
def CacheItemIns(self):
return self.__CacheItemIns
#CacheItemIns.setter
#abc.abstractmethod
def CacheItemIns(self, value):
self.__CacheItemIns = value
return
#abc.abstractmethod
def Load(self):
"""docstring"""
return
#abc.abstractmethod
def _deserializeCacheItem(self):
"""docstring"""
return
#abc.abstractmethod
def _deserializeNonCacheItem(self):
"""docstring"""
return
Cacheable.py
class Cacheable:
def _getFromCache(self, itemName, cacheType,
cachePath=None):
"""docstring"""
kwargs = {"itemName" : itemName,
"cacheType" : cacheType,
"cachePath" : cachePath}
lstSearchResult = CacheManager.SearchCache(**kwargs)
if lstSearchResult[0]:
self.CacheItemIns = lstSearchResult[1]
self._deserializeCacheItem()
else:
cacheItem = CacheManager.NewItem(**kwargs)
self.CacheItemIns = cacheItem
self._deserializeNonCacheItem()
return
SomeClass.py
import ICacheable
import Cacheable
class SomeClass(Cacheable, ICacheable):
__valueFromCache1:str = ""
__valueFromCache2:str = ""
__CacheItemIns:dict = {}
#property
def CacheItemIns(self):
return self.__CacheItemIns
#CacheItemIns.setter
def CacheItemIns(self, value):
self.__CacheItemIns = value
return
def __init__(self, itemName, cacheType):
#Call Method from Cacheable
self.__valueFromCache1
self.__valueFromCache2
self.__getItemFromCache(itemName, cacheType)
return
def _deserializeCacheItem(self):
"""docstring"""
self.__valueFromCache1 = self.CacheItemIns["val1"]
self.__valueFromCache2 = self.CacheItemIns["val2"]
return
def _deserializeNonCacheItem(self):
"""docstring"""
self.__valueFromCache1 = #some external function
self.__valueFromCache2 = #some external function
return
So this example works, but the scary thing is that there is no gurantee that a class inherriting Cacheable also inherits ICacheable. Which seems like a design flaw, as Cacheable is useless on its own. However the ability to abstract things from my subclass/child class with this is powerful. Is there a way to guarantee Cacheable's dependency on ICacheable?
If you explicitly do not want inheritance, you can register classes as virtual subclasses of an ABC.
#ICacheable.register
class Cacheable:
...
That means every subclass of Cacheable is automatically treated as subclass of ICacheable as well. This is mostly useful if you have an efficient implementation that would be slowed down by having non-functional Abstract Base Classes to traverse, e.g. for super calls.
However, ABCs are not just Interfaces and it is fine to inherit from them. In fact, part of the benefit of ABC is that it enforces subclasses to implement all abstract methods. An intermediate helper class, such as Cacheable, is fine not to implement all methods when it is never instantiated. However, any non-virtual subclass that is instantiated must be concrete.
>>> class FailClass(Cacheable, ICacheable):
... ...
...
>>> FailClass()
TypeError: Can't instantiate abstract class FailClass with abstract methods CacheItemIns, Load, _deserializeCacheItem, _deserializeNonCacheItem
Note that if you
always subclass as class AnyClass(Cacheable, ICacheable):
never instantiate Cacheable
that is functionally equivalent to Cacheable inheriting from ICacheable. The Method Resolution Order (i.e. the inheritance diamond) is the same.
>>> AnyClass.__mro__
(__main__. AnyClass, __main__.Cacheable, __main__.ICacheable, abc.ABC, object)