Many Python modules preserve an internal state without defining classes, e.g. logging maintains several loggers accessible via getLogger().
How do you test such a module? Using the standard unittest tools, I would like the various tests inside a TestCase class to re-import my module-under-test so that each time it loses its context. Can this be done?
import unittest
import sys
class Test(unittest.TestCase):
def tearDown(self):
try:
del sys.modules['logging']
except KeyError:
pass
def test_logging(self):
import logging
logging.foo=1
def test_logging2(self):
import logging
print(logging.foo)
if __name__ == '__main__':
unittest.sys.argv.insert(1,'--verbose')
unittest.main(argv = unittest.sys.argv)
% test.py Test.test_logging passes:
test_logging (__main__.Test) ... ok
but
% test.py Test.test_logging2 does not:
test_logging2 (__main__.Test) ... ERROR
since the internal state of logging has been reset.
This will reimport the module as new for you:
import sys
del sys.modules['my_module']
import my_module
Related
I have something like this in my source file
# code.py
def some_func():
# doing some connections and stuff
return {'someKey': 'someVal'}
class ClassToTest:
var = some_func()
My test file looks like this... I am trying to mock some_func as I want to avoid creating connections.
# test_code.py
from src.code import ClassToTest
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
#mock.patch('src.code.some_func', new=mock_function)
def test_ClassToTest(self):
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
But this doesn't work. On the other hand if var is an instant variable mock works fine. I guess this is due to class variables getting initialized during imports. How do I properly mock some_func before var even gets initialized?
When you imported code.py, there is no active patch yet, so when ClassToTest.var was initialized, it used the original some_func. Only then would the patch for src.code.some_func would take effect which obviously is too late now.
Solution 1
What you can do is to patch some_func and then reload code.py so that it re-initializes the ClassToTest including its attribute var. Thus since we already have an active patch by the time we reload code.py, then ClassToTest.var would be set with the patched value.
But we can't do it if both the class and the patched function lives in the same file, so to make it testable move some_func to another file and then just import it.
src/code.py
from src.other import some_func
class ClassToTest:
var = some_func()
src/other.py
def some_func():
# doing some connections and stuff
return {'realKey': 'realValue'}
test_code.py
from importlib import reload
import sys
import unittest
from unittest import mock
from src.code import ClassToTest # This will always refer to the unpatched version
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
def test_real_first(self):
self.assertEqual(ClassToTest.var, {"realKey": "realValue"})
#mock.patch('src.other.some_func', new=mock_function)
def test_mock_then_reload(self):
# Option 1:
# import src
# reload(src.code)
# Option 2
reload(sys.modules['src.code'])
from src.code import ClassToTest # This will be the patched version
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
def test_real_last(self):
self.assertEqual(ClassToTest.var, {"realKey": "realValue"})
Output
$ pytest -q
... [100%]
3 passed in 0.04s
Solution 2
If you don't want the real some_func to be ever called during test, then just reloading isn't enough. What needs to be done is to never import the file containing ClassToTest nor any file that would import it indirectly. Only import it once an active patch for some_func is already established.
from importlib import reload
import sys
import unittest
from unittest import mock
# from src.code import ClassToTest # Remove this import!!!
def mock_function():
return {"someOtherKey": "someOtherValue"}
class Test_Code(unittest.TestCase):
#mock.patch('src.other.some_func', new=mock_function)
def test_mock_then_reload(self):
from src.code import ClassToTest # Move the import here once the patch has taken effect already
self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
I'm trying to unit test a class which is derived from a base_class in an external module. In my dev/test environment I have not access to this external module, which means I have to somehow mock this base_class.
My test-code resides in a different file from the code I'm trying to test.
The problem can be summarized as follows:
my_class.py
import external_module
class MyClass(external_module.ExternalClass):
def test_method(self):
return "successful"
test_my_class.py
import sys
import unittest
from unittest.mock import MagicMock
sys.modules['external_module'] = MagicMock()
from my_class import MyClass
class TestMyClass(unittest.TestCase):
def test_first(self):
my_class = MyClass()
result = my_class.test_method()
self.assertEqual(result, "successful")
if __name__ == '__main__':
unittest.main()
Results
When running test_my_class.py the result are the following.
AssertionError: <MagicMock name='mock.ExternalClass.test_method()' id='140272215184664'> != 'successful'
Clearly since the external_module is mocked, even MyClass becomes an instance of a mock-object.
Similar posts
The problem is similar to as described in Python mock: mocking base class for inheritance, but has the difference that the base_class is from an external module.
Even How to mock a base class with python mock library show som similarities to my problem, though the solutions can not be directly applied.
Tries and failures
To get the import
import external_module
to work in my_class.py
sys.modules['external_module'] = MagicMock()
need to be set in test_my_class.py.
Though, this leads to that external_module.* becomes a Mock-instance.
You could create a helper module mocked_external_module, which can be imported from your tests and also contains a class base_class. Then you do the following in your test code:
import mocked_external_module
sys.modules['external_module'] = mocked_external_module
Plus, every method of your base_class that you need to mock you can create as a Mock or MagicMock.
I'm trying to test some code using pytest and need to change a function from some module. One of my imports also imports that function, but this is failing when I change the method using monkeypatch. Here is what I have:
util.py
def foo():
raise ConnectionError # simulate an error
return 'bar'
something.py
from proj import util
need_this = util.foo()
print(need_this)
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
import proj.something
And this raises ConnectionError. If I change
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
def test_test():
import proj.something
Then it imports with the monkeypatch working as expected. I've read though this and tried to model my testing from it, but that isn't working unless I import inside of a test. Why does the monkeypatch not do anything if it is just a normal import in the testing file?
That is because the fixture is applied to the test function not the entire code. autouse=True attribute just says that it should be used in every test
I would like to ensure with tests that:
- the application cannot be imported
- the application can be started as a real application (i.e: python src.py)
I'm interested about that, why the following is not working:
src.py
class A:
def x(self):
print('this is x')
if __name__ == '__main__':
A().x()
test.py (snippet)
class Test(unittest.TestCase):
#mock.patch('src.A.x')
def test_main(self, mock_x):
import src
mock_x.assert_any_call()
This test fails... why?
Because the name of the module when imported is src, not __main__.
The easiest solution would be to move that code into a function:
def main():
A().x()
if __name__ == '__main__':
main()
and in your test, you would invoke src.main()
#mock.patch('src.A.x')
def test_main(self, mock_x):
import src
src.main()
mock_x.assert_any_call()
To test that a module is not importable you do not need to use mocks.
See assertRaises.
Just check if an error is thrown on import od module.
with self.assertRaises(...):
...
I would like to mock a certain module in order to test a piece of code that is using the module.
That is to say, I have a module my_module which I'd like to test. my_module imports an external module real_thing and calls real_thing.compute_something():
#my_module
import real_thing
def my_function():
return real_thing.compute_something()
I need to mock real_thing so that in a test it will behave like fake_thing, a module that I've created:
#fake_thing
def compute_something():
return fake_value
The test calls my_module.my_function() which calls real_thing.compute_something():
#test_my_module
import my_module
def test_my_function():
assert_something(my_module.my_function())
What should I add to the test code so that my_function() will call fake_thing.compute_something() within the test instead of real_thing.compute_something()?
I was trying to figure out how to do so with Mock, but I haven't.
Simply that no ? Hack the sys.modules
#fake_thing.py
def compute_something():
return 'fake_value'
#real_thing.py
def compute_something():
return 'real_value'
#my_module.py
import real_thing
def my_function():
return real_thing.compute_something()
#test_my_module.py
import sys
def test_my_function():
import fake_thing
sys.modules['real_thing'] = fake_thing
import my_module
print my_module.my_function()
test_my_function()
Outputs : 'fake_value'
http://code.google.com/p/mockito-python/
>>> from mockito import *
>>> dog = mock()
>>> when(dog).bark().thenReturn("wuff")
>>> dog.bark()
'wuff'
http://technogeek.org/python-module.html - how to replace ,load module dynamically