How to suppress ImportWarning in a python unittest script - python-3.x

I am currently running a unittest script which successfully passes the various specified test with a nagging ImportWarning message in the console:
...../lib/python3.6/importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
return f(*args, **kwds)
....
----------------------------------------------------------------------
Ran 7 tests in 1.950s
OK
The script is run with this main function:
if __name__ == '__main__':
unittest.main()
I have read that warnings can be surpressed when the script is called like this:
python -W ignore:ImportWarning -m unittest testscript.py
However, is there a way of specifying this ignore warning in the script itself so that I don't have to call -W ignore:ImportWarning every time that the testscript is run?
Thanks in advance.

To programmatically prevent such warnings from showing up, adjust your code so that:
import warnings
if __name__ == '__main__':
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=ImportWarning)
unittest.main()
Source: https://stackoverflow.com/a/40994600/328469
Update:
#billjoie is certainly correct. If the OP chooses to make answer 52463661 the accepted answer, I am OK with that. I can confirm that the following is effective at suppressing such warning messages at run-time using python versions 2.7.11, 3.4.3, 3.5.4, 3.6.5, and 3.7.1:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import warnings
class TestPandasImport(unittest.TestCase):
def setUp(self):
warnings.simplefilter('ignore', category=ImportWarning)
def test_01(self):
import pandas # noqa: E402
self.assertTrue(True)
def test_02(self):
import pandas # noqa: E402
self.assertFalse(False)
if __name__ == '__main__':
unittest.main()
However, I think that the OP should consider doing some deeper investigation into the application code targets of the unit tests, and try to identify the specific package import or operation which is causing the actual warning, and then suppress the warning as closely as possible to the location in code where the violation takes place. This will obviate the suppression of warnings throughout the entirety of one's unit test class, which may be inadvertently obscuring warnings from other parts of the program.
Outside the unit test, somewhere in the application code:
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=ImportWarning)
# import pandas
# or_ideally_the_application_code_unit_that_imports_pandas()
It could take a bit of work to isolate the specific spot in the code that is either causing the warning or leveraging third-party software which causes the warning, but the developer will obtain a clearer understanding of the reason for the warning, and this will only improve the overall maintainability of the program.

I had the same problem, and starting my unittest script with a warnings.simplefilter() statement, as described by Nels, dit not work for me. According to this source, this is because:
[...] as of Python 3.2, the unittest module was updated to use the warnings module default filter when running tests, and [...] resets to the default filter before each test, meaning that any change you may think you are making scriptwide by using warnings.simplefilter(“ignore”) at the beginning of your script gets overridden in between every test.
This same source recommends to renew the filter inside of each test function, either directly or with an elegant decorator. A simpler solution is to define the warnings filter inside unittest's setUp() method, which is run right before each test.
import unittest
class TestSomething(unittest.TestCase):
def setUp(self):
warnings.simplefilter('ignore', category=ImportWarning)
# Other initialization stuff here
def test_a(self):
# Test assertion here.
if __name__ == '__main__':
unittest.main()

I had the same warning in Pycharm for one test when using unittest. This warning disappeared when I stopped trying to import a library during the test (I moved the import to the top where it's supposed to be). I know the request was for suppression, but this would also make it disappear if it's only happening in a select number of tests.

Solutions with def setUp suppress warnings for all methods within class. If you don't want to suppress it for all of them, you can use decorator.
From Neural Dump:
def ignore_warnings(test_func):
def do_test(self, *args, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
test_func(self, *args, **kwargs)
return do_test
Then you can use it to decorate single test method in your test class:
class TestClass(unittest.TestCase):
#ignore_warnings
def test_do_something_without_warning()
self.assertEqual(whatever)
def test_something_else_with_warning()
self.assertEqual(whatever)

Related

Monkeypatch setenv value persist across test cases in python unittest

I have to set different environment variables for my test cases.
My assumption was once the test case is completed, monkeypatch will remove the env variables from os.environ. But it is not. How to set and revert the environment variables for each test?
Here is my simplified test case code with monkeypatch lib.
import os
import unittest
import time
from _pytest.monkeypatch import MonkeyPatch
class Test_Monkey_Patch_Env(unittest.TestCase):
def setUp(self):
print("Setup")
def test_1(self):
monkeypatch = MonkeyPatch()
monkeypatch.setenv("TESTVAR1", "This env value is persistent")
def test_2(self):
# I was expected the env TESTVAR1 set in test_1 using monkeypatch
# should not persist across test cases. But it is.
print(os.environ["TESTVAR1"])
def tearDown(self):
print("tearDown")
if __name__ == '__main__':
unittest.main()
Output:
Setup
tearDown
.Setup
This env value is persistent
tearDown
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
This expands a bit on the (correct) answer given by #MaNKuR.
The reason why it does not work as expected is that in pytest, it is not designed to be used this way - instead the monkeypatch fixture is used, which does the cleanup on leaving the test scope. To do the cleanup in unittest, you can do that cleanup in tearDown, as shown in the answer - though I would use the less specific undo for this:
from _pytest.monkeypatch import MonkeyPatch
class Test_Monkey_Patch_Env(unittest.TestCase):
def setUp(self):
self.monkeypatch = MonkeyPatch()
def tearDown(self):
self.monkeypatch.undo()
This reverts any changes made by using the MonkeyPatch instance, not just the specific setenv call shown above, and is therefore more secure.
Additionally there is the possibility to use MonkeyPatch as a context manager. This comes in handy if you want to use it just in one or a couple of tests. In this case you can write:
...
def test_1(self):
with MonkeyPatch() as mp:
mp.setenv("TESTVAR1", "This env value is not persistent")
do_something()
The cleanup in this case is done on exiting the context manager.
To remove env variable set by monkeypatch module, seems you have to call delenv method.
You can call this delenv after you are done with setting & testing env variable using setenv method but I think tearDown would be the right place to put that delenv call.
def tearDown(self):
print("tearDown")
monkeypatch.delenv('TESTVAR1', raising=False)
I have not tested the code however should give you fair amount of idea whats needs to be done.
EDIT: Improved code to use setup * tearDown more efficiently. test_2 should raise OS Error since env variable has been deleted
import os
import unittest
import time
from _pytest.monkeypatch import MonkeyPatch
class Test_Monkey_Patch_Env(unittest.TestCase):
def setUp(self):
self.monkeypatch = MonkeyPatch()
print("Setup")
def test_1(self):
self.monkeypatch.setenv("TESTVAR1", "This env value is persistent")
def test_2(self):
# I was expected the env TESTVAR1 set in test_1 using monkeypatch
# should not persist across test cases. But it is.
print(os.environ["TESTVAR1"]) # Should raise OS error
def tearDown(self):
print("tearDown")
self.monkeypatch.delenv("TESTVAR1", raising=False)
if __name__ == '__main__':
unittest.main()
Cheers!

PyQt5 GUI freeze caused by Windows focus-follows-mouse

When Windows focus-follows-mouse-without-raising-the-window is enabled by either of the two methods linked to below, I consistently get PyQt5 GUI 'freezes' where you have to type any character in the terminal that you ran python from in order to unfreeze the GUI; complete description and test case (Windows 10, Python 3.6.1, PyQt5) is here: pyqt5 click in terminal causes GUI freeze
To enable the focus-follows-mouse-without-raise behavior, try either of these - they both work in Windows 10:
downloadable program ('X-Mouse' though that name is used by other programs):
https://joelpurra.com/projects/X-Mouse_Controls/
registry hack description:
https://sinewalker.wordpress.com/2010/03/10/ms-windows-focus-follows-mouse-registry-hacks/
So - a few questions:
can anyone reproduce the issue? It seems 100% reproducible for me, but it would be great to hear the same from someone else.
is there a way to change the python code to detect-and-circumvent focus-follows-mouse, or just to be immune to it, i.e. maybe by ensuring the GUI application always takes focus back again when you - for example - click in a dialog or qmessagebox owned by the main GUI window, or by some other means? (Is the object hierarchy set up optimally, and if not, maybe this could all be resolved by correcting the ownership structure?)
The brute-force solution seems to work, though I'd like to leave this question open to see if someone knows of a more optimal solution; it took a fair amount of searching to figure out the right way; mainly by taking a look a the open-source code for X-Mouse. Basically, this method takes effect immediately, whereas the registry hack doesn't take effect until reboot.
New version of pyqt_freeze_testcase.py (the file from the referenced stackoverflow question); the changes are only additions, noted between lines of hash marks:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
####################### added begin:
import win32gui
import win32con
####################### added end
# import the UI file created with pyuic5
from minimal_ui import Ui_Dialog
class MyWindow(QDialog,Ui_Dialog):
def __init__(self,parent):
QDialog.__init__(self)
self.parent=parent
self.ui=Ui_Dialog()
self.ui.setupUi(self)
################################# added begin:
self.initialWindowTracking=False
try:
self.initialWindowTracking=win32gui.SystemParametersInfo(win32con.SPI_GETACTIVEWINDOWTRACKING)
except:
pass
if self.initialWindowTracking:
print("Window Tracking was initially enabled. Disabling it for now; will re-enable on exit.")
win32gui.SystemParametersInfo(win32con.SPI_SETACTIVEWINDOWTRACKING,False)
################################# added end
def showMsg(self):
self.really1=QMessageBox(QMessageBox.Warning,"Really?","Really do stuff?",
QMessageBox.Yes|QMessageBox.No,self,Qt.WindowTitleHint|Qt.WindowCloseButtonHint|Qt.Dialog|Qt.MSWindowsFixedSizeDialogHint|Qt.WindowStaysOnTopHint)
self.really1.show()
self.really1.raise_()
if self.really1.exec_()==QMessageBox.No:
print("nope")
return
print("yep")
################################## added begin:
def closeEvent(self,event):
if self.initialWindowTracking:
print("restoring initial window tracking behavior ("+str(self.initialWindowTracking)+")")
win32gui.SystemParametersInfo(win32con.SPI_SETACTIVEWINDOWTRACKING,self.initialWindowTracking)
################################## added end
def main():
app = QApplication(sys.argv)
w = MyWindow(app)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How to import modules from a separate directory?

I want to import asyncore from a different directory, because I need to make some changes to how asyncore works, and don't want to modify the base file.
I could include it in the folder with my script, but after putting all the modules I need there it ends up getting rather cluttered.
I'm well aware of making a sub directory and putting a blank __init__.py file in it. This doesn't work. I'm not exactly sure what happens, but when I import asyncore from a sub directory, asyncore just plain stops working. Specifically; the connect method doesn't get run at all, even though I'm calling it. Moving asyncore to the main directory and importing it normally removes this problem.
I skimmed down my code significantly, but this still has the same problem:
from Modules import asyncore
from Modules import asynchat
from Modules import socket
class runBot(asynchat.async_chat, object):
def __init__(self):
asynchat.async_chat.__init__(self)
self.connect_to_twitch()
def connect_to_twitch(self):
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(('irc.chat.twitch.tv',6667))
self.set_terminator('\n')
self.buffer=[]
def collect_incoming_data(self, data):
self.buffer.append(data)
def found_terminator(self):
msg = ''.join(self.buffer)
print(msg)
if __name__ == '__main__':
# Assign bots to channels
bot = runBot()
# Start bots
asyncore.loop(0.001)
I'm sure this is something really simple I'm overlooking, but I'm just not able to figure this out.
Use sys.path.append -- see https://docs.python.org/3/tutorial/modules.html for the details.
Update: Try to put a debug print to the beginning and end of sources of your modules to see whether they are imported as expected. You can also print __file__ attribute for the module/object that you want to use to see, whether you imported what you expected -- like:
import re
#...
print(re.__file__)

restart python (or reload modules) in py.test tests

I have a (python3) package that has completely different behaviour depending on how it's init()ed (perhaps not the best design, but rewriting is not an option). The module can only be init()ed once, a second time gives an error. I want to test this package (both behaviours) using py.test.
Note: the nature of the package makes the two behaviours mutually exclusive, there is no possible reason to ever want both in a singular program.
I have serveral test_xxx.py modules in my test directory. Each module will init the package in the way in needs (using fixtures). Since py.test starts the python interpreter once, running all test-modules in one py.test run fails.
Monkey-patching the package to allow a second init() is not something I want to do, since there is internal caching etc that might result in unexplained behaviour.
Is it possible to tell py.test to run each test module in a separate python process (thereby not being influenced by inits in another test-module)
Is there a way to reliably reload a package (including all sub-dependencies, etc)?
Is there another solution (I'm thinking of importing and then unimporting the package in a fixture, but this seems excessive)?
To reload a module, try using the reload() from library importlib
Example:
from importlib import reload
import some_lib
#do something
reload(some_lib)
Also, launching each test in a new process is viable, but multiprocessed code is kind of painful to debug.
Example
import some_test
from multiprocessing import Manager, Process
#create new return value holder, in this case a list
manager = Manager()
return_value = manager.list()
#create new process
process = Process(target=some_test.some_function, args=(arg, return_value))
#execute process
process.start()
#finish and return process
process.join()
#you can now use your return value as if it were a normal list,
#as long as it was assigned in your subprocess
Delete all your module imports and also your tests import that also import your modules:
import sys
for key in list(sys.modules.keys()):
if key.startswith("your_package_name") or key.startswith("test"):
del sys.modules[key]
you can use this as a fixture by configuring on your conftest.py file a fixture using the #pytest.fixture decorator.
Once I had similar problem, quite bad design though..
#pytest.fixture()
def module_type1():
mod = importlib.import_module('example')
mod._init(10)
yield mod
del sys.modules['example']
#pytest.fixture()
def module_type2():
mod = importlib.import_module('example')
mod._init(20)
yield mod
del sys.modules['example']
def test1(module_type1)
pass
def test2(module_type2)
pass
The example/init.py had something like this
def _init(val):
if 'sample' in globals():
logger.info(f'example already imported, val{sample}' )
else:
globals()['sample'] = val
logger.info(f'importing example with val : {val}')
output:
importing example with val : 10
importing example with val : 20
No clue as to how complex your package is, but if its just global variables, then this probably helps.
I have the same problem, and found three solutions:
reload(some_lib)
patch SUT, as the imported method is a key and value in SUT, you can patch the
SUT. Example, if you use f2 of m2 in m1, you can patch m1.f2 instead of m2.f2
import module, and use module.function.

Mocking print but allow using it in tests

Print can be mocked in the following way:
import unittest
import builtin
class TestSomething(unittest.TestCase):
#mock.patch('builtins.print')
def test_method(self, print_):
some_other_module.print_something()
However this means that in the python debug console (pydev debugger) and in the unit test method itself print cannot be used. This is rather inconvenient.
Is there a way to only mock the print method in some_other_module instead of in the testing module as well?
A way to sidestep this is to swap the use of print in the test module with some other function which just calls print, which I can do if there turns out to be no better solution.
#michele's "final solution" has an even cleaner alternative which works in my case:
from unittest import TestCase
from unittest.mock import patch
import module_under_test
class MyTestCase(TestCase):
#patch('module_under_test.print', create=True)
def test_something(self, print_):
module_under_test.print_something()
print_.assert_called_with("print something")
Yes you can! ... But just because you are using Python 3. In Python 3 print is a function and you can rewrite it without change the name. To understand the final solution I'll describe it step by step to have a final flexible and non intrusive solution.
Instrument Module
The trick is add at the top of your module that you will test a line like:
print = print
And now you can patch just print of your module. I wrote a test case where the mock_print_module.py is:
print = print
def print_something():
print("print something")
And the test module (I'm using autospec=True just to avoid errors like mock_print.asser_called_with):
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
I don't want to change my module but just patch print without lose functionalities
You can use patch on "builtins.print" without lose print functionality just by use side_effect patch's attribute:
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
Now you can trace your prints call without lose the logging and pydev debugger. The drawback of that approach is that you must fight against lot of noise to check your interested the print calls. Moreover you cannot chose what modules will be patched and what not.
Both modes don't work together
You cannot use both way together because if you use print=print in your module you save builtins.print in print variable at the load module time. Now when you patch builtins.print the module still use the original saved one.
If you would have a chance to use both you must wrap original print and not just record it. A way to implement it is use following instead of print=print:
import builtins
print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
The Final Solution
Do we really need to modify the original module to have a chance to patch all print calls in it? No, we can do it without change the module to test anyway. The only thing that we need is injecting a local print function in the module to override the builtins's one: we can do it in the test module instead of the the module to test. My example will become:
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
import builtins
mock_print_module.print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
and mock_print_module.py can be the clean original version with just:
def print_something():
print("print something")

Resources