isinstance mocked pythonnet class - python

I am making some python unit tests over classes which import C# with pythonnet, but mocks of those imported elements do not pass isinstance() tests.
Code under test:
import clr
clr.AddReference('MyDll')
from MyDll import ClassNet
def check(object_net):
assert isinstance(object_net, ClassNet) # <-- fails
...
Test code:
import unittest
from unittest.mock import MagicMock
from MyCode import check
class Test(unittest.TestCase):
def test(self):
mock_object = MagicMock(spec=ClassNet)
check(mock_object)
Test fails because isinstance return false although spec argument has been used. Based on Mock Doc:
If spec is an object (rather than a list of strings) then __class__ returns the class of the spec object. This allows mocks to pass isinstance() tests.
Any idea whether I am missing something?
Debugging, ClassNet is a CLR Metatype, but I don't know if this has something to do with it.

Related

Is it possible to prevent a module/global class variable from creating instance for unit test?

I need to write test cases for the module
to_be_tested.py
from module_x import X
_x = X() # creating X instance in test environment will raise error
#.....
In the test case,
from unittest import TestCase, mock
class Test1(TestCase):
#mock.patch('...to_be_tested._x')
#mock.patch('...to_be_tested.X.func1')
def test_1(self, mock_func1, mock_x):
...
However, this will not prevent the import from creating the instance. Is it a way to workaround it and write the test cases for the module? Or is it a way to refactory to_be_tested to be testable?
Maybe write in to_be_tested.py, just _x = None if detected test environment?
The instantiation of X at the global level seems problematic, but I don't have the full picture so I can't definitively say "don't do that". If you can refactor it so that the X() instance is created as needed or something along those lines, that would be ideal.
That said, here's a way to prevent module_x from being imported during the test. I'm basing that on the assumption that X() is used throughout the module_x module, so there's really no need for anything in that module and you only want to mock it.
import sys
import unittest
from unittest import TestCase, mock
class Test1(TestCase):
#classmethod
def setUpClass(cls):
sys.modules['module_x'] = mock.Mock()
#mock.patch('to_be_tested._x')
#mock.patch('to_be_tested.X.func1')
def test_1(self, mock_func1, mock_x):
from to_be_tested import _x
print(_x)
You can see that _x is a mock now, but note that you can't have the import outside of your tests (like at the top of the test module, like most imports usually are) because sys.modules['module_x'] hasn't been subbed out yet.
One possibility is to guard the creation of _x with an environment variable so that you can disable its initialization in test mode. For example,
import os
from module import X
_x = None
if 'TEST' not in os.environ:
_x = X()
Now, you just need to ensure that TEST is set in your environment before importing to_be_tested. This is proabably something you would do in with your test runner, but it's also possible to do directly in your test module.
from unittest import TestCase, mock
import os
os.environ['TEST'] = ''
import to_be_tested
class Test1(TestCase):
...

How to elegantly mock a class using pytest fixtures?

Let's say I have a fixture to mock a class using monkeypatch.
# conftest.py
#pytest.fixture
def mock_dummyclass(monkeypatch):
def mock_function():
return None
monkeypatch.setattr(dummypackage, "dummyclass", mock_function)
Now, I have to use this fixture in my test so that this class is mocked.
#test_dummy.py
#pytest.mark.usefixtures("mock_dummyclass")
class TestManyDummyMethods:
def test_dummy_one():
import dummypackage # Flag A
def test_dummy_two():
import dummypackage # Flag B
...
def test_dummy_n():
import dummypackage # Flag N
As you can see in the flagged lines, that I'll have to import this module dummypackage inside every function to ensure that the fixture is applied before the module is imported (otherwise the fixture would not be effective).
Importing dummypackage inside the class would work but calling self.dummypackage inside functions doesn't seem very elegant either.
Is there a more elegant way to achieve this?
Comment: The monkeypatch library doesn't seem to be maintained anymore. unittest.mock should probably offer all you need.
I would try to avoid a solution that depends on importing the module as part of your test because that would break if it was imported for another reason (e.g. import other functions).
I am using os as an example as that exists and makes it reproducible.
How best to patch seem to depend on how you import from another module.
Example 1: target_module1.py (importing the join method from os.path):
from os.path import join
def some_method_using_join():
return join('parent', 'child')
This requires us to patch the join method of target_module1.py:
target_module1_test.py:
from unittest.mock import patch, MagicMock
import pytest
from target_module1 import some_method_using_join
#patch('target_module1.join')
def some_test(join_mock: MagicMock):
some_method_using_join()
Example 2: target_module2.py (importing the os module):
import os
def some_method_using_join():
return os.path.join('parent', 'child')
This allows us to patch the join method on os.path:
target_module2_test.py:
from unittest.mock import patch, MagicMock
import pytest
from target_module2 import some_method_using_join
#patch('os.path.join')
def some_test(join_mock: MagicMock):
some_method_using_join()
This assumes that you don't need to patch a class or method that is used at the module level (i.e. while importing).

How to mock external module calls and test code in the class body

I want to store static information in a class, shared by all the instances. The information is something obtained by using another module, I only want to do this once, eg. let's say that this is what I have in mymodule.py:
import os
MyClass:
bus = os.environ.get('DBUS_SESSION_BUS_ADDRESS', None)
def __init__(self):
pass
How can I test this code and mock os.environ.get to make sure that the call is made correctly?
Since the execution happens at the time of the first import, I would need to reload the module in the test, but even so, I can't have os.environ.get mocked at the right time:
import unittest
from unittest import patch, MagicMock
import importlib
import mymodule
class TestMyClass(unittest.TestCase):
#patch('mymodule.os.environ', spec=['get'])
def test_class_init_patch(self, mock_env):
# Too early - the reload overrides the mock
importlib.reload(mymodule)
mock_env.get.assert_called_once_with('DBUS_SESSION_BUS_ADDRESS', None)
def test_class_init_mock(self):
importlib.reload(mymodule)
# Too late - the class body already executed
mymodule.MyClass.os.environ = MagickMock()
I have managed to come up with two alternatives, that make this testable:
Move the class initialization code into a class method and call it from the class body. I can test this class method in my unit test.
Move the class initialization into the __init__ method, guarded by a flag stored in a class variable so that it is only initialized once on the first instantiation.
While both of these should work just fine, it just feels more clean and straightforward to leave this in the class body if possible.
I have managed to figure out how to do this the right way (I hope)!
If you have imported only the module and not the class, function into your code's namespace, eg.:
like this: import os
and not like this: from os import geteuid
You can do this in your test to patch the os module directly:
import os
import unittest
from unittest import patch
import importlib
import mymodule
class TestMyClass(unittest.TestCase):
#patch('os.environ.get')
def test_class_init_patch(self, mock_env_get):
importlib.reload(mymodule)
mock_env_get.assert_called_once_with('DBUS_SESSION_BUS_ADDRESS', None)
This is described in the official documentation as well:
However, consider the alternative scenario where instead of from a import SomeClass module b does import a and some_function uses a.SomeClass. Both of these import forms are common. In this case the class we want to patch is being looked up in the module and so we have to patch a.SomeClass instead:
More details in the official documentation.
This way the module is being patched directly and reloading your own module doesn't affect the patching.
Once the test has run, the patching of the module is undone as usual, however, keep in mind, that the state of your class remains the same way as it was initialized while the external module was patched, so you might need to reload the module again before you run your other tests.
The easiest way to make sure, that you reclaim your "normal" class state, is to reload the module in your TestCase object's setUp method, eg.:
def setUp(self):
importlib.reload(mymodule)

Why is mock.patch class decorator not working?

I want to mock something for an entire class but the following minimal example is not working:
import time
import six
if six.PY2:
import mock
else:
from unittest import mock
#mock.patch('time.sleep', mock.Mock(side_effect=Exception('dooooom')))
class Foo(object):
def bar(self):
print('before')
time.sleep(1)
print('after')
f = Foo()
f.bar()
I get this unexpected output: (why did time.sleep not raise?)
before
after
However if I move the #mock.patch(...) down 1 line so it is decorating the method bar instead of the class Foo then it works as expected:
before
...
Exception: blah
Why does #mock.patch not work at the class level?
It turns out the class decorator only patches methods beginning with patch.TEST_PREFIX which defaults to test.
So renaming the method to test_bar or even testbar makes the patch start working.
Docs:
Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch() finds tests by looking for method names that start with patch.TEST_PREFIX. By default this is 'test', which matches the way unittest finds tests. You can specify an alternative prefix by setting patch.TEST_PREFIX.
Evidently this behaviour applies to any class whether inheriting from unittest.TestCase or not.

Overloading unittestTestCase

I have a module myTest.py in which I am going to overload the unittest.TestCase class. So in a testfile, tests are created like follows:
import myTest
class MyTest(myTest.MyTestCase):
...do something...
This works fine, but when I need other functionality from unittest, like unittest.skip, I also need to invoke unittest as well. I want to be able to invoke just myTest.skip, which should be a reference to unittest.skip. How can I achieve this behavior for every unittest attribute (except the inheritance from unittest.TestCase)? Is there another way instead doing from unittest import * in myTest.py?
In myTest.py:
from unittest import skip
Then you can do:
import myTest
#myTest.skip(...)
class OtherTest(myTest.MyTestCase):
...

Categories

Resources