How to Mock class with other class in Python unittest - python

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.

Related

getting circular import while trying hinting python

I have 2 class (that are defined in two different package).
An A object as a "set" of B objects that all refer to the said A object.
Here is how it looks like :
the a.py :
from b import B
class A():
def __init__(self, data):
self.data = data
self.Bs = {}
def add_B(self, id, data_B):
self.Bs[id] = B(data_B, self)
the b.py :
class B():
def __init__(self, data, a_instance):
self.data = data
self.a = a_instance
so everything works preety good, but I'd like to hint python that the a_instance is indeed a class A object to have autocompletion in visual studio code.
At first i've tried to add from a import A and modify def __init__(self, data, a_instance : A): in the b.py file, but i've obviously got a circular import error
So I've been trying to use the typing package, and so added those lines to the a.py file :
from typing import NewType
A_type = NewType('A_type', A)
But I'm steel getting a circular import error.
Can Anyone explain me what I'm doing wrong ?
thanks for the help
PS: My classes actually have some complex methods and are defined in _a.py (resp. _b.py) and the __init__.py just import the class A and declare the A_type (resp. just import the class B)
Use
typing.TYPE_CHECKING, a variable that's never true at runtime
the string form of a type annotation to refer to a name that is not in scope at runtime:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from a import A
class B:
def __init__(self, data, a_instance: "A"):
...
However, if you can restructure your code in a way that avoids circular imports altogether, all the better.
You could try an abstract class with attributes of A and B, then implement each accordingly.
from collections.abc import abstractmethod, ABCMeta
class ABInerface(metaclass=ABCMeta):
#property
#abstractmethod
def data(self):
pass
#data.setter
#abstractmethod
def data(self, value):
pass
#property
#abstractmethod
def Bs(self):
pass
#Bs.setter
#abstractmethod
def Bs(self, value):
pass
#property
#abstractmethod
def a(self):
pass
#a.setter
#abstractmethod
def a(self, value):
pass
#abstractmethod
def add_B(self, id, data_B):
pass
Then, create each class by extending the Interface meta class.
class B(ABInerface):
def __init__(self, data, a_instance):
self.data = data
self.a = a_instance
class A(ABInerface):
def __init__(self, data):
self.data = data
def add_B(self, id, data_B):
self.Bs[_id] = B(data_B, self)

How to replace a class?

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.

Inheritance of dynamically added methods

I have the following class ClassA, for which I dynamically create a method returning string version of another method:
# module_a.py
class ClassA(object):
def width(self):
return 5
def height(self):
return 10
#classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self):
return str(method(self))
setattr(cls, '{0}_str'.format(name), str_method)
for name in ['width', 'height']:
ClassA.add_str_method(name)
This part works perfectly fine as long as I don't subclass ClassA in a different module. But when I do, like in the example below, the dynamically added methods are not inherited.
# module_b.py
from module_a import ClassA
class ClassB(ClassA):
pass
What would be the proper way of adding methods dynamically such that they are automatically inherited by subclasses?
First you have to declare add_str_method as a #classmethod if you want to update the class A dynamically (not just an instance of A).
# file a.py
class A(object):
def __init__(self, a=5):
self._a = a
def a(self):
return self._a
#classmethod
def add_str_method(cls, name):
def str_method(self):
return str(getattr(self, name)())
setattr(cls, '{0}_str'.format(name), str_method)
for name in ['a']:
A.add_str_method(name)
In order to access the a method from A, and thus the variable _a attached to a particular instance, the str method has to be bounded to self, note this lines:
def str_method(self):
return str(getattr(self, name)())
Now, with this testing script it works as expected:
# file b.py
from a import A
class B(A):
pass
print(B(10).a_str()) # prints '10'
You'll have to add the procedure to the initialization (__init__) of classA:
class ClassA(object):
def __init__(self):
for name in ['width', 'height']:
self.add_str_method(name)
def width(self):
return 5
def height(self):
return 10
def add_str_method(cls, name):
method = getattr(self, name)
def str_method(self):
return str(method())
setattr(cls, '{0}_str'.format(name), str_method)
Now doing
from module_a import ClassA
class ClassB(ClassA):
pass
print(dir(ClassB))
Gives:
>>> ... 'height', 'width']

Is this a correct way of using dependencies in python

I am learning python and I am facing a dilemma:
from abc import ABCMeta, abstractproperty, abstractmethod
import jsocket
from acme.core import StatusCode, Direction
import acme.db as db
class ModuleThread(jsocket.ServerFactoryThread):
__metaclass__ = ABCMeta
#abstractproperty
def module_name(self):
pass
#abstractproperty
def _db_model(self):
pass
def __init__(self):
super(ModuleThread, self).__init__()
self.code = StatusCode.PENDING
self.status = None
def _get_config_for_domain(self, domain, direction):
# here I want to be sure that my db model is an instance
# of a pewee model
print self._db_model
class CheckMxThread(ModuleThread):
#property
def module_name(self):
return 'check_mx'
#property
def _db_model(self):
return db.ModMx
And the call
CheckMxThread()._get_config_for_domain('nocheck.mx', Direction.INCOMING)
I want to be sure that when using self._db_model I get an instance of Pewee Model, how should I handle this:
directly by using the top import like I've done
Injecting db package in the ModuleThread class and using later as self.db.ModMx in child classes ?
You can verify this by using isinstance:
#property
def _db_model(self):
if not instanceof(self, db.ModMx):
raise IOError('can not use this instance type')
This work with child inhertance, example:
>>> class A:
... pass
>>> class B(A):
... pass
>>> b = B()
>>> isinstance(b, A)
True

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