Run unittest from a Python program via a command-line option - python

Here is my set up -
project/
__init__.py
prog.py
test/
__init__.py
test_prog.py
I would like to be able to run my unit tests by calling a command-line option in prog.py. This way, when I deploy my project, I can deploy the ability to run the unit tests at any time.
python prog.py --unittest
What do I need in prog.py, or the rest of my project for this to work?

The Python unittest module contains its own test discovery function, which you can run from the command line:
$ python -m unittest discover
To run this command from within your module, you can use the subprocess module:
#!/usr/bin/env python
import sys
import subprocess
# ...
# the rest of your module's code
# ...
if __name__ == '__main__':
if '--unittest' in sys.argv:
subprocess.call([sys.executable, '-m', 'unittest', 'discover'])
If your module has other command-line options you probably want to look into argparse for more advanced options.

Perhaps this is what you're looking for. Implement a load_tests function in test_prog.py and use the following code in prog.py to load and run the tests:
import unittest
import test.test_prog
suite = unittest.TestLoader().loadTestsFromModule(test.test_prog)
unittest.TextTestRunner().run(suite)

You must make sure that you consistently follow some naming conventions (which you seem to be doing):
All tests are named with the same prefix (test_ is the norm), followed by the name of the module you wish to test.
prog.py => test_prog.py
Tests reside in test/ directory.
Then you can do something like this:
prog.py
import sys
...
... do module stuff here...
...
if __name__ == "__main__":
# Check if we want to run the tests for this file
if "--unittest" in sys.argv:
import unittest
test_filename = 'test_' + __file__
test_directory = 'test'
suite = unittest.TestLoader().discover(test_directory, pattern=test_filename)
unittest.TextTestRunner(verbosity=2).run(suite)
What we are doing, is:
Checking the command arguments to see if --unittest is present (since that's the only time you want to run the tests).
If it is, then we create the test_prog.py - following the naming conventions we have set.
Then we pass that to the TestLoader().discover function.
discover(...) starts at the specified directory and finds all test modules (recursing into subdirectories ) that match the pattern provided.
In our case, it will look inside the test/ directory for any module named test_prog.py. When it does, it loads it and creates a TestSuite with the TestCases that we want to run.
Lastly, we manually test unittest to run the suite obtained in the previous step.
Normally, unittest will do all of this for us in the background, but since we are trying to run a specific test module, we have to tell exactly how and where to get it from.
Also, note that you will have to do this for every file where you want to do this at.

I just removed the .idea hidden directory under the project in the file system.
I restarted PyCharm, reopened the project, and this issue was resolved.

Related

Module not found running on command line

I have the following project structure:
project/
example/
__init__.py
foo.py
boo.py
meh.py
tests/
example/
test_foo.py
test_boo.py
test_meh.py
As example, I'm importing foo.py in boo.py as import example.foo as f. And I'm running tests with python3 -m pytest -s -v --cov tests on root folder (project). The unit tests are running very smooth but when I try to run a single file as python3 example/boo.py I got an error:
ModuleNotFoundError: No module named 'example'
Modules inside a package shouldn't really be run (some exceptions).
But, you can set a PYTHONPATH before running the module if you really want. For a one off, use e.g.
PYTHONPATH=$(pwd) python3 example/boo.py
An alternative is to use relative imports: from . import foo as f inside boo.py. But that still implies that modules shouldn't really be run.
To elaborate a bit more:
A module should be imported, not run like a script. That is what a module is for. If, for some reason, you really really feel you need to execute module, then 1/ reconsider, 2/ rewrite your module, 3/ wrap a script around that module by calling the necessary function(s) inside the module (and keep the script itself relatively short).
Note that setuptools already has this functionality through entry points.
A simpler alternative is to use a proper
if __name__ == '__main__':
main()
line at the end of your module, where main() calls into your module functionality, then execute the module using the Python -m switch:
python -m mypackage.mymodule
But, again, try and limit this functionality.
It's usually a problem with environment variables. You can force the path using the following, and the import should work under all circumstances:
import sys
sys.path.append("/absolute/module/path")
import local_module

Where to place python unittests

I have a directory structure as follows:
DirA
__init__.py
MyClass.py
unittests <------------------directory
MyClassTest.py
MyClassTest.py is executable:
import unittest
from . import MyClass
class MyClassTestCase(unittest.TestCase):
""" Testcase """
...
.....
if __name__ == '__main__':
unittest.main()
I get an error "Parent module '' not loaded, cannot perform relative import" at the line:
from . import MyClass
I would like to place unittests in a 'unittests' directory beside the modules being tested. Is there a way to do this and have access to all the modules in the parent directory which I am testing?
Have you tried running the tests like so:
cd DirA
python -m unittest discover unittests "*Test.py"
This should find your modules correctly. See Test Discovery
Use whatever layout you want, depending on your own preferences and the way you want your module to be imported:
http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html#the-double-import-trap
https://pytest.org/latest/goodpractises.html
To find your unittests folder, since the name is not the conventional one (unit test scripts by default look for a test folder), you can use the discover option of the unittest module to tell how to find your test scripts:
python -m unittest discover unittests
Note that the first unittest is the Python module, and the second unittests (with an s) is your directory where you have placed your testing scripts.
Another alternative is to use the nosetest module (or other new unit testing modules like pytest or tox) which should automatically find your testing script, wherever you place them:
nosetests -vv
And to fix your import error, you should use the full relative (or absolute) path:
from ..MyClass import MyClass # Relative path from the unittests folder
from MyClass import MyClass # Absolute path from the root folder, which will only work for some unit test modules or if you configure your unit test module to run the tests from the root
A suggested structure, would be to look at your structure like this:
my_app
my_pkg
__init__.py
module_foo.py
test
__init__.py
test_module_foo.py
main.py
Run everything from within my_app, this way you will use all the same module references between your test code and core code.

Nosetests Import Error

I'm trying to use nosetests to run my tests in a directory structure like this
src
- file1.py
- ...
test
- helper.py
- test_file1.py
As you can see, test_file1.py has some functions that test file1.py, so it imports file1.py like this:
# In file1.py
import file1
import helper
# Tests go here...
I also use a helper.py file that has some neat functionality built in so that I can create tests more easily. This functionality is achieved by extending a couple of classes in my actual code and overriding some methods. So helper.py looks something like this:
# In helper.py
import file1
# Use stuff in file1.py
I'm having trouble understanding how nose goes about importing these things with its custom importer. I was able to get my test file to import file1.py by running nosetest ../tests within the src directory, but I'm currently getting an error akin to:
File helper.py:
ImportError: cannot import name file1
How does nose do its imports and is there a way I can essentially get it to lump all my tests/src files together so they can all import one another while I keep them in separate folders?
Seeing that you execute tests with nosetests ../tests I assume they are executed from the tests folder itself. Therefore, files from the src directory are not added to sys.path, hence the error.
To fix this one could:
run tests from the parent directory - nosetests will be able to identify src and test (or tests) directory by himself and will add them to the sys.path before running tests
add src directory path to the PYTHONPATH before running nosetests (export PYTHONPATH=../src; nosetests)
Note that you can as well omit the last argument to the nosetests as by default it runs the tests from current directory. Otherwise, if the tests are not in the directory you launch nosetests from, you can define its location with --where=<path-to-tests> parameter (or, simply -w). So for example you can execute tests from src direcotory and without even setting the PYTHONPATH (because current directory will be added to sys.path by default) like this: nosetests -w ../tests.
Lastly, even though this is very questionable by itself, and yet: the most common way to organize a Python source code is having python files and packages starting directly in the project directory, and having tests in "test" sub-packages of the packages they test. So, in your case it would be:
/file1.py
/test/helper.py
/test/test_file1.py
or better:
/myproject/__init__.py
/myproject/file1.py
/myproject/test/__init__.py
/myproject/test/helper.py
/myproject/test/test_file1.py
(latter, provided you also use correct imports in your test sources, e.g. from .. import file1).
In which case one runs tests from the project's root directory simply with nosetests without any argument.
Anyway, nosetests is flexible enough to work with any structure - use whatever seems more suitable for you and the project.
More on project structure in What is the best project structure for a Python application?
This seems generally like an issue I had with nose tests:
Importing with Python and Nose Tests
The work around I found was to insert a try..except block so that BOTH python and nosetest commands will work on the same directory as follows:
(1) In your main file, at the very top before anything else add:
# In file1.py
try:
# This will allow you to do python file1.py inside the src directory
from file2 import *
from helper import *
except:
# This will allow you to run nosetests in the directory just above
# the src and test directories.
from src.file1 import *
from src.helper import *
(2) Inside your test.py file add:
from src.file2 import *
from src.helper import *

How to write tests in Python using unittest

I want to write my library in TDD methodology, but I have no idea how to design directory structure (or how to use unittest). Now I create a directory tree such as:
myproject (directory)
- tests (directory)
- src (directory)
- test.py (file running tests)
Each class from src has its own unittest class. Each directory has its own __init__.py file. I want to run tests only from test.py file and thanks to this each test can from src.modulename import classname and than runs test unittest.main() function. Unfortunately it doesn't work (runs zero tests).
Is this good approach? What are my mistakes?
The code in file test.py should look like:
from tests import *
import unittest
if __name__ == '__main__':
testsuite = unittest.TestLoader().discover('.')
unittest.TextTestRunner(verbosity=1).run(testsuite)
This code copies all tests from tests directory, because it copies entire package. The main method runs all test methods included in tests package's classes. Each test file name must start with test.

python unittests, do I need to import from the tests/__init__.py?

I have my code organized like this:
base_project_dir/src
/tests
/test1.py
/test2.py
in test1.py and test2.py I have the classes extending unittest.TestCase
according to python API doc, I should be able to run
python -m unittest tests from base dir and run all tests.
but doing so shows that it finds 0 tests. so I added
from test1 import *
from test2 import *
into tests/__init__.py
now the above command works. but when I want to run individual tests, it sources the module init, which forcefully run all tests.
what is the correct way to organize this?
thanks
Yang
Try this in your main project dir:
python -m unittest discover -v
This way you don't change the location for your relative imports. When you do python -m unittest tests it switches to this directory first and then can't import the projects code anymore.

Categories

Resources