Chained imports for unit tests in python - python

There are a lot of threads on SO about unit tests and module imports, but still I'm having trouble importing modules into my testing script, when the module that I'm testing itself imports anoter module.
Here's my project structure:
Here's util1:
def add(x, y):
return x + y
if __name__ == "__main__":
pass
Here's util2:
from util1 import add
def add_then_string(x, y):
z = add(x, y)
return str(z)
if __name__ == "__main__":
pass
And here's the test file:
import unittest
class TestProject(unittest.TestCase):
def test_dummy(self):
self.assertTrue(True)
def test_add(self):
from Scripts.Project.util1 import add
self.assertEqual(add(1,1), 2)
def test_add_then_string(self):
from Scripts.Project.util2 import add_then_string
self.assertEqual(add_then_string(1,1), "2")
if __name__ == "__main__":
unittest.main()
The problem is that util2 can't find util1 when it's called from the test file via Scripts.Project.
I've put __init__.py files everywhere, but that doesn't help.
Here's the error when I run the tests:
Error
Traceback (most recent call last):
File path\to\dummy_test_project\UnitTests\test_project.py", line 13, in test_add_then_string
from Scripts.Project.util2 import add_then_string
File path\to\dummy_test_project\Scripts\Project\util2.py", line 1, in <module>
from util1 import add
ModuleNotFoundError: No module named 'util1'
How do I make this work so that I can test the add_then_string function in util2?
EDIT: As so often happens, something has occurred to me immediately after I post on SO. When I change the import in util2 to
from Scripts.Project.util1 import add
then the tests pass. My real repository has lots and lots of imports. Do I need to prepend Scripts.Project to all of them to get this to work?

Related

if __name__ == '__main__' function call

I am trying to work around a problem I have encountered in a piece of code I need to build on. I have a python module that I need to be able to import and pass arguments that will then be parsed by the main module. What I have been given looks like this:
#main.py
if __name__ == '__main__'
sys.argv[] #pass arguments if given and whatnot
Do stuff...
What I need is to add a main() function that can take argument(s) and parse them and then pass them on like so:
#main.py with def main()
def main(args):
#parse args
return args
if __name__ == '__main__':
sys.argv[] #pass arguments if given and whatnot
main(sys.argv)
Do stuff...
To sum up: I need to import main.py and pass in arguments that are parsed by the main() function and then give the returned information to the if __name_ == '__main_' part.
EDIT
To clarify what I am doing
#hello_main.py
import main.py
print(main.main("Hello, main"))
ALSO I want to still be able to call main.py from shell via
$: python main.py "Hello, main"
Thus preserving the name == main
Is what I am asking even possible? I have been spending the better part of today researching this issue because I would like to, if at all possible, preserve the main.py module that I have been given.
Thanks,
dmg
Within a module file you can write if __name__ == "__main__" to get specific behaviour when calling that file directly, e.g. via shell:
#mymodule.py
import sys
def func(args):
return 2*args
#This only happens when mymodule.py is called directly:
if __name__ == "__main__":
double_args = func(sys.argv)
print("In mymodule:",double_args)
One can then still use the function when importing to another file:
#test.py
import mymodule
print("In test:",mymodule.func("test "))
Thus, calling python test.py will result in "In test: test test ", while calling python mymodule.py hello will result in "In mymodule: hello hello ".

Mocking __main__

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(...):
...

Importing values in config.py

I wanted to mix a config.py approach and ConfigParser to set some default values in config.py which could be overridden by the user in its root folder:
import ConfigParser
import os
CACHE_FOLDER = 'cache'
CSV_FOLDER = 'csv'
def main():
cp = ConfigParser.ConfigParser()
cp.readfp(open('defaults.cfg'))
cp.read(os.path.expanduser('~/.python-tools.cfg'))
CACHE_FOLDER = cp.get('folders', 'cache_folder')
CSV_FOLDER = cp.get('folders', 'csv_folder')
if __name__ == '__main__':
main()
When running this module I can see the value of CACHE_FOLDER being changed. However when in another module I do the following:
import config
def main()
print config.CACHE_FOLDER
This will print the original value of the variable ('cache').
Am I doing something wrong ?
The main function in the code you show only gets run when that module is run as a script (due to the if __name__ == '__main__' block). If you want that turn run any time the module is loaded, you should get rid of that restriction. If there's extra code that actually does something useful in the main function, in addition to setting up the configuration, you might want to split that part out from the setup code:
def setup():
# the configuration stuff from main in the question
def main():
# other stuff to be done when run as a script
setup() # called unconditionally, so it will run if you import this module
if __name__ == "__main__":
main() # this is called only when the module is run as a script

Python unittesting: run tests in another module

I want to have the files of my application under the folder /Files, whereas the test units in /UnitTests, so that I have clearly separated app and test.
To be able to use the same module routes as the mainApp.py, I have created a testController.py in the root folder.
mainApp.py
testController.py
Files
|__init__.py
|Controllers
| blabla.py
| ...
UnitTests
|__init__.py
|test_something.py
So if in test_something.py I want to test one function that is in /Files/Controllers/blabla.py, I try the following:
import unittest
import Files.Controllers.blabla as blabla
class TestMyUnit(unittest.TestCase):
def test_stupid(self):
self.assertTrue(blabla.some_function())
if __name__ == '__main__':
unittest.main()
And then from the file testController.py, I execute the following code:
import TestUnits.test_something as my_test
my_test.unittest.main()
Which outputs no failures, but no tests executed
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
[Finished in 0.3s]
I have tried with a test that has no dependences, and if executed as "main" works, but when called from outside, outputs the same:
import unittest
def tested_unit():
return True
class TestMyUnit(unittest.TestCase):
def test_stupid(self):
self.assertTrue(tested_unit())
if __name__ == '__main__':
unittest.main()
Question: how do I get this to work?
The method unittest.main() looks at all the unittest.TestCase classes present in the context.
So you just need to import your test classes in your testController.py file and call unittest.main() in the context of this file.
So your file testController.py should simply look like this :
import unittest
from UnitTests.test_something import *
unittest.main()
In test_something.py, do this:
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMyUnit, 'test'))
return suite
In testController.py, do this:
from TestUnits import test_something
def suite():
suite = unittest.TestSuite()
suite.addTest(test_something.suite())
return suite
if __name__ == '__main__':
unittest.main(defaultTest='suite')
There is a workaround of using subprocess.call() to run tests, like:
import subprocess
args = ["python", "test_something.py"]
subprocess.call(args)

Running unit tests from the tested module

I have a bunch of modules and for each module I have a unittest based test. I want to define the main in each module to run its tests, but I get import errors because of import loops (specifically when I use from mymodule import myclass in the test.
I suspect this is a solved problem, so - what should I put in my module's main to run its corresponding test?
If I understand you correctly, you've got a file (lets call it mymodule.py) that looks like this:
import unittest
from mymoduletests import MyModuleTests
class myclass(object):
def somefunction(self, x):
return x*x
if __name__ == '__main__':
unittest.main()
and a separate file (lets call it mymoduletests.py) that looks something like this:
import unittest
from mymodule import myclass
class MyModuleTests(unittest.TestCase):
def test_somefunction(self):
m = myclass()
self.assertEqual(4, m.somefunction(2))
If you run mymodule.py you get the following result:
Traceback (most recent call last):
File "mymodule.py", line 2, in <module>
from mymoduletests import MyModuleTests
File "/Users/srgerg/Desktop/p/mymoduletests.py", line 2, in <module>
from mymodule import myclass
File "/Users/srgerg/Desktop/p/mymodule.py", line 2, in <module>
from mymoduletests import MyModuleTests
ImportError: cannot import name MyModuleTests
However, if you change mymodule.py to this:
class myclass(object):
def somefunction(self, x):
return x*x
if __name__ == '__main__':
import unittest
from mymoduletests import MyModuleTests
unittest.main()
and then run it, you get:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Have I understood you correctly?

Categories

Resources