How to replace a class? - python

I am writing test files for a Python module
class A:
def func(self):
B().sub_func()
class B:
def sub_func(self):
pass
and I need block B's side effect while testing A.
my question is that how to replace class B in test file.
class ATest(unittest.TestCase):
def test_a(self):
a=A()
a.func()
#now object a will invoke a func that B is a mock class
following the instruction of accepted answer. I finish my tests
tws/main.py
class A(object):
def func(self):
b=B()
print('func ')
b.sub_func()
class B(object):
def sub_func(self):
print('real sub')
c=C()
c.c_sub_func()
class C(object):
def c_sub_func(self):
print('c')
test/test_mock.py
import unittest
from unittest.mock import patch
from tws.main import A
class B():
def sub_func(self):
print('mock')
return 12
class TestMock(unittest.TestCase):
#patch('tws.main.B',new=B)
def test_af(self):
a=A()
print(a.__dict__)
a.func()
print('rrr')
it will print mock,hope it will help other people with same problems.

As suggested in the comments, you can mock out class B by using unittest.mock - in particular patch().
You can use patch() as a decorator on your test method, making sure you add an extra argument to receive the mocked class B:
from unittest.mock import patch
class ATest(unittest.TestCase):
#patch('package.module.B')
def test_a(self, mock_b): # mock_b will hold the mocked class B
a = A()
a.func()
Note that the path you pass to the decorator must be the path to where B is used (the same module containing class A) not the path to where class B is defined. See Where to patch for more info on that.

Related

How to Mock class with other class in Python unittest

I'm trying to mock a class in python with another class using unittest but the unittest.patch creates an instance from the mock class and replaces the original class with it. Here is the description
The Origin class is located in the file: src/libutil/util.py
class A:
def __init__(self) -> None:
self.a = self.g()
self.c = "parent"
def g(self):
return "HI from parent"
The mock class is located in the file tests/libraries/mocks/util.py
class B(A):
def __init__(self) -> None:
super().__init__()
def g(self):
return "Hi from child"
I'm mocking that using unittest as follows:
#pytest.fixture(scope="session", autouse=True)
def mock_util():
from tests.libraries.mocks.util import B
with mock.patch('libutil.util.A', new_callable=B, create=False) as util_mock:
yield util_mock
The problem is that the patch creates an instance from class B and replaces class A with it instead of replacing class A with class B itself. When I use a = libutil.util.A() that doesn't work and throws TypeError: 'B' object is not callable.
Can you help me in mocking class A with class B itself? Please note that the usage here is a simplified example.

In Python, is there any way to call a child class's method override from its parent class?

I'm trying to re-teach myself Python and figure out the specific details, tips and tricks, and common conventions around abstract classes and polymorphism. Right now, I have a class hierarchy that looks like this:
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def x(self):
pass
def do_x_ten_times(self):
for i in range(10):
x()
class B(A):
def x(self):
print("Hello World")
class C(A):
def x(self):
print("Hello StackOverflow")
b = B()
b.x()
c = C()
c.x()
b.do_x_ten_times()
My thinking is that do_x_ten_times() would be the same exact code in both subclasses B and C. So it would be convenient (and my code would be less repetitive) if I could just put the code for do_x_ten_times() in A, and have A call whatever the subclass's implementation of x() is. Unfortunately, I get "NameError: name 'x' is not defined."
I get why might not be typical to do something like this, and my gut says that it probably goes against certain rules of polymorphism. If I really need to, I'm fine copypastying do_x_ten_times() into both classes B and C, and making it abstract in A. But I'm wondering if there's any reasonable way around having to repeat this code.
You need to call self.x() in A.do_x_ten_times()
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def x(self):
raise NotImplementedError
def do_x_ten_times(self):
for i in range(10):
self.x() # <-- self will refer to the calling instance
# implementation of x(self)
class B(A):
def x(self):
print("Hello World")
class C(A):
def x(self):
print("Hello StackOverflow")
b = B()
b.x()
c = C()
c.x()
b.do_x_ten_times()

Calling different parent-class methods with one decorator

So basically my problem seems like this.
class A():
def func(self):
return 3
class B():
def func(self):
return 4
class AA(A):
def func(self):
return super(AA, self).func
class BB(B):
def func(self):
return super(BB, self).func
The func function is doing some work and one of the things it does is getting some attribute(or running method or whatever) from it's parent class.
Since func originally does the same logic at both cases (except that only parent class changes) I'd like to do this with decorators.
Is it possible? if so how to do it? Do I have somehow to pass parent-class as a argument?
I'll be very grateful for answers it's been bothering me for a while now.
There is no need to use super to access data attributes of a parent class.
Neither does a class need a parent in order for access to data attributes to work.
You can use a mixin to do the job:
# A and B stay the same - they still have a c attribute
class A():
c = 3
class B():
c = 4 # I've changed B to make it clear below
#Instead have a mixin which defines func()
class Mixin:
def func(self):
# func has its behaviour here
return self.c
class AA(Mixin, A):
pass
class BB(Mixin, B):
pass
a = AA()
b = BB()
print(a.func())
print(b.func())
Output:
3
4
You could do it with a single class decorator by defining a generic method inside of it that does what you want, and then adding it to the class being decorated. Here's what I mean:
def my_decorator(cls):
def call_super_func(self):
return super(type(self), self).func()
setattr(cls, 'call_super_func', call_super_func)
return cls
class A():
def func(self):
print('in A.func')
return 3
class B():
def func(self):
print('in B.func')
return 4
#my_decorator
class AA(A):
def func(self):
print('in AA.func')
return self.call_super_func()
#my_decorator
class BB(B):
def func(self):
print('in BB.func')
return self.call_super_func()
aa = AA()
aa.func()
bb = BB()
bb.func()
Output:
in AA.func
in A.func
in BB.func
in B.func
Of course you could eliminate the need to do this by just defining baseclass for A and B that has a call_super_func() method in it that they would then both inherit.

Identify the superclass that defines a class-level variable

In the case of multiple inheritance in python, is there a way to identify which super class a class-level variable is obtained from?
All attempts I tried to google are overwhelmingly about How to get the attribute not find out where it came from:
https://www.google.com/search?q=pythin+which+super+class+defines+attr
https://www.google.com/search?q=python+which+super+class+has+attribute&oq=python+which+super+class+has+attr
https://www.google.com/search?q=python+which+super+class+attribute+obtained+from
I suppose I can manually step through the MRO using inspect.getmro(cls). But I couldn't find any more elegant solutions. Just wondering if anyone knows of one.
EDIT
For a concrete example:
class Super1(object):
__class_attribute__ = "Foo"
class Super2(object):
pass
class Derived(Super1, Super2):
pass
d = Derived()
parent_cls = some_function_to_get_defining_class(d.__class_attribute__) # <-- should return `Super1`
The __qualname__ attribute gives an indication from which class a method was inherited. However, this only returns a string, not the superclass itself. If you need to the superclass for metaprogramming, I think you are going to have to dig into the MRO.
class A:
def a(self):
return 1
def b(self):
return 2
class B:
def b(self):
return 2.5
def c(self):
return 3
class C(A,B):
pass
Using:
C.b.__qualname__
# returns:
'A.b'
However, this does not apply when using abstract methods to define an interface, since the method has to be overwritten.
from abc import abstractmethod
class A:
def a(self):
return 1
#abstractmethod
def b(self):
pass
class C(A):
def b(self):
return 100
C.b.__qualname__
# returns:
'C.b'

How can I override class call inside of an imported class in python?

Let say I have the following script in modul1:
class IN(object):
def __init__(self):
pass
class C(object):
def __init__(self, x):
pass
def func(self):
cl = IN()
Then I want to use C class inside another script:
from modul1 import C
class IN(object):
def __init__(self):
pass
class C2(C):
def __init__(self, x):
C.__init__(self, x)
I can override C class's func method by creating a method with the same name in C2 class.
But how can I override any call of modul1's IN class inside of imported C class with IN class in the caller modul2?
I want to change some functionality of original IN class. I want C class to call in the row
cl = IN()
my own IN() class with the altered functionality.
module1.py:
class IN(object):
def __init__(self):
print "i am the original IN"
class C(object):
def __init__(self, x):
pass
def func(self):
print "going to create IN from C's func"
cl = IN()
module2.py:
import module1
class IN(object):
def __init__(self):
print "I am the new IN"
class C2(module1.C):
def __init__(self, x):
super(C2, self).__init__(x)
print "\n===Before monkey patching==="
C2(1).func()
#monkey patching old In with new In
module1.IN = IN
print "\n===After monkey patching==="
C2(1).func()
Output while running the script module2.py:
===Before monkey patching===
going to create IN from C's func
i am the original IN
===After monkey patching===
going to create IN from C's func
I am the new IN
You can see how the module2's In constructor is being called.

Categories