How to write tests in Python using unittest - python

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.

Related

What is the proper way to write unit tests on modules (in subdirectories) which use other modules in the parent directory in Python

Tried to describe it as best as I could in the title. I'm quite new to using python for a big project. Usually, I only use it for small scripts. I read through the manual and found out what is the proper way to make modules.
So I have the following file structure set up now:
- main.py
- module1
- __init__.py
- thing1.py
- thing2.py
- module2
- __init__.py
- thingA.py
- thingB.py
My project will run when I start main.py.
I know I can write simple unit tests for every 'thing' by using the if __name__ == '__main__': in every one of them which is what I do.
The file thingB.py needs thing2.py to work. As long as main.py is started, thingB will find thing2. But when I start thingB directly (to make its unit test run) in its own directory, it won't find thing2.
What is the proper approach for this?
You should run everything from your main directory:
python -m module1.thing1
python -m module2.thingA
this way module1 and module2 can import from each other. Unit tests should be run in the same way:
python -m unittest discover module1
python -m unittest discover module2

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 *

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

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.

Structuring Python OOP Code and Modules

packageName\
__init__.py
src\
__init__.py
someFile.py
classes\
__init__.py
engine1.py
engine2.py
engine.py
tests\
__init__.py
myTests.py
temp\
I'm working on OOP with Python, and I have a few questions.
I understand that __init__.py defines the folder as a module, but what I do not understand is how this benefits me.
How would I run myTests.py if it needs to import a class from the packageName/src/classes folder?
$ python packageName/tests/myTests.py
The above call is how I guess I'd intend to run my tests. If the structure is the case, how can I import the classes?
from ..src.classes.engine1 import *
As #Mike mentioned, The Hitchhiker's Guide to Packaging would be a great place to start.
Now, as far as your tests go: for various Reasons, you can't just run python packageName/tests/myTests.py (specifically, Python will get confused about the package layout — when you run a file with python foo.py, it's expected to be outside a package, so the from ..src … line will fail with Attempted relative import in non-package).
Instead, you need to start your tests from outside the package… Either using a test runner like nose or unittest2, or writing a simple script in the top level directory (ie, above packageName/):
$ ls
packageName/ run_tests
$ cat run_tests.py
from packageName.tests.myTests import main
main()
Then put a main() function in myTests().
However, using a test runner is much simpler. If you've written your tests correctly, you can run them with:
$ ls
packageName/
$ nosetests
..................................
----------------------------------------------------------------------
Ran 34 tests in 1.440s
OK

Categories

Resources