I'm fairly new to python and currently attempting to write a unit test for a class, but am having some problems with mocking out dependencies. I have 2 classes, one of which (ClassB) is a dependency of the other (ClassC). The goal is to mock out ClassB and the ArgumentParser classes in the test case for ClassC. ClassB looks as follows:
# defined in a.b.b
class ClassB:
def doStuff(self) -> None:
# do stuff
pass
def doSomethingElse(self) -> None:
# do something else
pass
ClassC:
# defined in a.b.c
from .b import ClassB
from argparse import ArgumentParser
class ClassC:
b
def __init__(self) -> None:
arguments = self.parseArguments()
self.b = ClassB()
self.b.doStuff()
def close(self) -> None:
self.b.doSomethingElse()
def parseArguments(self) -> dict:
c = ArgumentParser()
return return parser.parse_args()
And finally, the test case for ClassC:
# inside a.b.test
from unittest import TestCase
from unittest.mock import patch, MagicMock
from a.b.c import ClassC
class ClassCTest(TestCase):
#patch('a.b.c.ClassB')
#patch('a.b.c.ArgumentParser')
def test__init__(self, mock_ArgumentParser, mock_ClassB):
c = ClassC()
print(isinstance(c.b, MagicMock)) # outputs False
# for reference
print(isinstance(mock_ClassB, MagicMock)) # outputs True
I read in the patch docs that it's important to mock the class in the namespace it is used not where it is defined. So that's what I did, I mocked: a.b.c.classB instead of a.b.b.classB, have tried both though. I also tried importing ClassC inside the test__init__ method body, but this also didn't work.
I prefer not mocking methods of ClassB but rather the entire class to keep the test as isolated as possible.
Environment info:
Python 3.6.1
Any help would be greatly appreciated!
Since i'm new to python i didn't know about class attributes. I had a class attribute in ClassC that held ClassB and an instance attribute in init that shadowed the class attribute.
Related
I have two .py files, one for the main(main.py) module and the other containing a class and its subclass (sub.py). From the main file, I use the class as follows.
## (main.py)
# Import the superclass
from sub import Class1
# Import the subclass
from sub import Class2
# Assign the object (it gives an error as described below)
myVariable=Class2()
where I write the two classes in sub.py as
## (sub.py)
class Class1:
def __init__(self, nugget=0):
self.eigval=nugget
self.eigvec=nugget
self.explained=nugget
class Class2(Class1):
def __init__(self, nugget=0):
super().__init__(eigval, eigvec, explained)
self.ppc=nugget
self.vol=nugget
self.corr=nugget
self.cov=nugget
The error I'm getting is
NameError: name 'eigval' is not defined
although I an inheriting the variable eigval using the super() in the subclass.
Why would that be??
You don't need to pass anything other than nugget to Class2
class Class2(Class1):
def __init__(self, nugget=0):
super().__init__(nugget)
self.ppc=nugget
self.vol=nugget
self.corr=nugget
self.cov=nugget
You are otherwise correct that super().__init__ will call the __init__ from Class1 and therefore your Class2 instance will have eigval, eigvec, and explained members.
>>> c = Class2()
>>> c.eigval
0
I am trying to properly create mocks for a class that has a dependency on a system library. Currently the code makes a connection to a socket for the library when being tested, and I am trying to remove that dependency.
class A:
SETTING_VARIABLE = "CONFIG_VALUE"
def __init__(self):
self.system_connector = library.open(self.SETTING_VARIABLE)
import A
class B:
INSTANCE_OF_A = A()
When class A is instantiated, it uses SETTING_VARIABLE to connect to a system library, which it can not do during a unit test, and my test suite fails during test collection. The library connector I am using can be configured to run in unit test mode, but requires a different configuration to be passed, so in this case SETTING_VARIABLE would need to be instantiated to "TEST_VALUE".
My test class test_B is failing as soon as B is imported when it tries to make a connection to the system library (I have disabled access to the socket for it). How can I set up Python mocking so that I can replace the value of the static variable defined by A?
One thing that I have tried to do from test_B:
import A
A.SETTING_VARIABLE = "TEST_VALUE"
This does seem to work, however is there a cleaner way to do this for unit tests?
Old but gold. Here is how this problem can be solved.
The B class needs to be changed to initialize INSTANCE_OF_A in its constructor. Then you can mock the SETTING_VARIABLE using patch.object.
Complete example with patch.object as instruction or decorator:
A.py:
class A:
SETTING_VARIABLE = "CONFIG_VALUE"
def __init__(self):
self.system_connector = library.open(self.SETTING_VARIABLE)
B.py:
from A import A
class B:
def __init__(self):
self.INSTANCE_OF_A = A()
test_B.py
import unittest
from unittest.mock import patch
from A import A
from B import B
class BTestCase(unittest.TestCase):
#patch.object(A, "SETTING_VARIABLE", "TEST_VALUE")
def test_b_with_decorator(self):
INSTANCE_OF_B = B()
def test_b_with_instruction(self):
with patch.object(A, "SETTING_VARIABLE", "TEST_VALUE"):
INSTANCE_OF_B = B()
Let's say I have this situation:
module2.py
class Bar:
def bar():
a = 5
# do stuff
Messages.show("Done")
module1.py
import module2
class Foo:
def __init__(self):
self.bar = module2.Bar()
def foo(self):
self.bar.bar()
I want to test the method Foo.foo(), but I want to ignore Messages.show("Done), ie I want calls to the Messages.show function to be done on a mock object. If foo was calling Messages.show directly, I could use monkeypatch on foo to mock the Messages class. But now, I'm calling a class from another module and I don't know how to specify that Messages.show calls should not be done ( the reason being that they access the Gui and that doesn't work in a test environment). Let's assume I cannot modify module2.py.
Just override what module2 thinks Messages is:
import module2
module2.Messages = ...
In my Python project, I’m extending scipy.stats.rv_continuous like this:
class GenlogisticGen(LmomDistrMixin, scipy.stats.rv_continuous):
...
I’m trying to build documentation on Read the Docs and am getting build errors:
class GenlogisticGen(LmomDistrMixin, scipy.stats.rv_continuous):
TypeError: metaclass conflict: the metaclass of a derived class must
be a (non-strict) subclass of the metaclasses of all its bases
Note that I’m mocking out the scipy.stats module as per the Read the Docs FAQ.
I guess by mocking out the base class something goes wrong. But what?
I had the same problem today, but with a Qt-related class. The problem is that the Mock class suggested by ReadTheDocs has a different metaclass to what's expected. Here's a description of the problem and the solution where what you want is a base class inheriting from object:
# =============================================================================
# Part 1. Set up the mock (you would put this in conf.py for Sphinx/autodoc).
# =============================================================================
import os
import sys
from unittest.mock import MagicMock
class Mock(MagicMock):
"""
Mock class that gives whatever attribute it's asked for, as per
https://docs.readthedocs.io/en/latest/faq.html. Intended to be used when
you can't genuinely install/import a module because ReadTheDocs (RTD)
doesn't allow the installation of modules with C (rather than pure Python)
code.
"""
#classmethod
def __getattr__(cls, name: str):
return MagicMock()
class SimpleClass(object):
"""
Dummy base class to replace a :class:`Mock` version; see
``FIX_THE_PROBLEM`` below.
"""
pass
MOCK_MODULES = [
# Things that ReadTheDocs won't install, but we want:
'PyQt5',
'PyQt5.QtCore',
'PyQt5.QtGui',
'PyQt5.QtNetwork',
'PyQt5.QtWidgets',
]
ON_READTHEDOCS = os.environ.get('READTHEDOCS') == 'True' # the normal test
ON_READTHEDOCS = True # for testing!
if ON_READTHEDOCS:
# Insert copies of our Mock class for modules we want to fake.
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
MODULE_MEMBERS_TO_MAKE_SIMPLE_CLASS = (
('PyQt5.QtCore', 'QAbstractListModel'),
('PyQt5.QtCore', 'QAbstractTableModel'),
# etc.
)
FIX_THE_PROBLEM = False # to see the problem, or True to fix it!
if FIX_THE_PROBLEM:
for module_name, class_name in MODULE_MEMBERS_TO_MAKE_SIMPLE_CLASS:
setattr(sys.modules[module_name], class_name, SimpleClass)
# =============================================================================
# Part 2. Simulate some user code.
# =============================================================================
from PyQt5.QtCore import QAbstractListModel
print(QAbstractListModel)
# For real: <class 'PyQt5.QtCore.QAbstractListModel'>
# If ON_READTHEDOCS: <MagicMock id='139789117901176'>
# If FIX_THE_PROBLEM too: <class '__main__.SimpleClass'>
print(type(QAbstractListModel))
# For real: <class 'sip.wrappertype'>
# If ON_READTHEDOCS: <class 'unittest.mock.MagicMock'>
# If FIX_THE_PROBLEM too: <class 'type'>
class MyRandomMixin(object):
pass
class MyDerived(QAbstractListModel, MyRandomMixin):
pass
# For real: it's happy.
# If ON_READTHEDOCS: will not create MyDerived; will crash with:
# TypeError: metaclass conflict: the metaclass of a derived class must be a
# (non-strict) subclass of the metaclasses of all its bases
# If ON_READTHEDOCS and FIX_THE_PROBLEM: happy again.
I have a base class that defines a class attribute and some child classes that depend on it, e.g.
class Base(object):
assignment = dict(a=1, b=2, c=3)
I want to unittest this class with different assignments, e.g. empty dictionary, single item, etc. This is extremely simplified of course, it's not a matter of refactoring my classes or tests
The (pytest) tests I have come up with, eventually, that work are
from .base import Base
def test_empty(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={})
assert len(Base().assignment.values()) == 0
def test_single(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
This feels rather complicated and hacky - I don't even fully understand why it works (I am familiar with descriptors though). Does mock automagically transform class attributes into descriptors?
A solution that would feel more logical does not work:
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = mock.PropertyMock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
or just
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = {'a':1}
assert len(Base().assignment.values()) == 1
Other variants that I've tried don't work either (assignments remains unchanged in the test).
What's the proper way to mock a class attribute? Is there a better / more understandable way than the one above?
base.Base.assignment is simply replaced with a Mock object. You made it a descriptor by adding a __get__ method.
It's a little verbose and a little unnecessary; you could simply set base.Base.assignment directly:
def test_empty(self):
Base.assignment = {}
assert len(Base().assignment.values()) == 0
This isn't too safe when using test concurrency, of course.
To use a PropertyMock, I'd use:
with patch('base.Base.assignment', new_callable=PropertyMock) as a:
a.return_value = {'a': 1}
or even:
with patch('base.Base.assignment', new_callable=PropertyMock,
return_value={'a': 1}):
Perhaps I'm missing something, but isn't this possible without using PropertyMock?
with mock.patch.object(Base, 'assignment', {'bucket': 'head'}):
# do stuff
To improve readability you can use the #patch decorator:
from mock import patch
from unittest import TestCase
from base import Base
class MyTest(TestCase):
#patch('base.Base.assignment')
def test_empty(self, mock_assignment):
# The `mock_assignment` is a MagicMock instance,
# you can do whatever you want to it.
mock_assignment.__get__.return_value = {}
self.assertEqual(len(Base().assignment.values()), 0)
# ... and so on
You can find more details at http://www.voidspace.org.uk/python/mock/patch.html#mock.patch.
If your class (Queue for example) in already imported inside your test - and you want to patch MAX_RETRY attr - you can use #patch.object or simply better #patch.multiple
from mock import patch, PropertyMock, Mock
from somewhere import Queue
#patch.multiple(Queue, MAX_RETRY=1, some_class_method=Mock)
def test_something(self):
do_something()
#patch.object(Queue, 'MAX_RETRY', return_value=1, new_callable=PropertyMock)
def test_something(self, _mocked):
do_something()
Here is an example how to unit-test your Base class:
mocking multiple class attributes of different types (ie: dict and int)
using the #patch decorator and pytest framework with with python 2.7+ or 3+.
# -*- coding: utf-8 -*-
try: #python 3
from unittest.mock import patch, PropertyMock
except ImportError as e: #python 2
from mock import patch, PropertyMock
from base import Base
#patch('base.Base.assign_dict', new_callable=PropertyMock, return_value=dict(a=1, b=2, c=3))
#patch('base.Base.assign_int', new_callable=PropertyMock, return_value=9765)
def test_type(mock_dict, mock_int):
"""Test if mocked class attributes have correct types"""
assert isinstance(Base().assign_dict, dict)
assert isinstance(Base().assign_int , int)