how to load resource file while running python unittest using twisted trial - python

Problem
As part of python unittest, some input json files are to be loaded which exists under 'data' directory which resides in the same directory of test py file.
'pkg_resources' is used for this purpose.
It works fine when the unittest are running with python. But it fails when running with twisted trial.
My project has mixed testcases with both python unittest testcases as well as twisted.trial.unittest testcases. so, there is a need to run both type of testcases with twisted trial in general.
The '_trial_temp' directory is added in path when running testcases with twisted trial. please, let me know there is any way to handle this?
Example directory structure:
myproject/
└── tests
├── data
│ └── input.json
├── trialTest.py
trialTest.py
import unittest
import inspect
import pkg_resources
class Test(unittest.TestCase):
def test_01_pathTest(self):
dataDirExists = pkg_resources.resource_exists(inspect.getmodule(self).__name__, 'data')
print 'data exists: %s' % (dataDirExists)
if __name__ == '__main__':
unittest.main()
Running test using python and its output:
cd myproject
python tests/trialTest.py
data exists: True
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Running test using python and its output:
cd myproject
/usr/local/bin/trial tests/trialTest.py
trialTest
Test
test_01_pathTest ... data exists: False
[OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.013s
PASSED (successes=1)

In the first example, __name__ will be set to __main__, and the tests directory will be automatically added to sys.path. This works more or less by accident; if you move your unittest.main invocation to another module, you won't be able to import it quite the same way, and data may not show up.
In the second example, trial will, depending on the presence of an __init__.py file in the tests directory, set __name__ to either trialTest or tests.trialTest; or perhaps even myproject.tests.trialTest.
You should re-name the module to test_trialtest.py so that it is discovered by trial's module-walking code properly, and then invoke it with a module name rather than a file name. This means you should have a clear idea of what myproject/tests/test_trialtest.py's module name is supposed to be. Is myproject supposed to be on sys.path? The parent directory?
Basically, pkg_resources depends intimately on the details of the namespaces into which code is loaded and executed, so you need to be careful that everything is set up consistently. If you make sure that everything is imported the same way, under the same name (never as __main__, for example) then this should be totally consistent between trial and stdlib unittest; there's nothing really special about trial here except that you are running it (trial itself)` as the main script rather than your test script as the main script.

place an __init__.py in tests directory resolves the issue.
[durai#localhost myproject]$ touch tests/__init__.py
[durai#localhost myproject]$ tree
.
├── tests
   ├── data
   │   └── input.json
   ├── __init__.py
   ├── trialTest.py
   └── trialTest.pyc
[durai#localhost myproject]$ trial tests/trialTest.py
tests.trialTest
Test
test_01_pathTest ... currModule: <module 'tests.trialTest' from '/home/durai/Worskspace/myproject/tests/trialTest.pyc'>
currModule: tests.trialTest
data exists: True
[OK]
------------------------------------------------------------------------------ -
Ran 1 tests in 0.016s
PASSED (successes=1)

Related

unittest directory structure - cannot import src code

I have the following folder structure in my project:
my-project
src/
__init__.py
script.py
test/
__init__.py
test_script.py
Ideally I want to have a separate folder where all the unit tests go. My test_script.py looks something like this:
from src.script import my_object
class TestClass(unittest.TestCase):
def test_script_object(self):
# unit test here
pass
if __name__ == 'main':
unittest.main()
When I try to run the script (using python test_script.py) I get the following error:
Traceback (most recent call last):
File "test_script.py", line 4, in <module>
from src.script import my_object
ModuleNotFoundError: No module named 'src'
I was following the instructions from this other thread, and I even tried appending src to the sys path (which forces me to change how I do imports in the rest of the project). When I'm not trying to append to the sys path, both of my __init__.py files are empty.
I am using python 3.8.
Does anyone have any suggestions? I'm new to unit testing in python, so maybe there is a better structure or other conventions I'm not aware of. Thanks in advance for your help!
Generally, any instructions that have you modifying sys.path in order to run your tests are sending you in the wrong direction. Your testing tool should be able to discover both your tests and your application code without requiring that sort of hackery.
I generally use pytest for running my tests. I would structure your example like this:
my-project/
├── src
│   ├── conftest.py
│   └── myproject
│   ├── __init__.py
│   └── script.py
└── tests
├── __init__.py
└── test_script.py
Assuming that src/myproject/script.py looks like this:
def double(x: int):
return x*2
And tests/test_script.py look like this:
import myproject.script
def test_double():
res = myproject.script.double(2)
assert res == 4
I can run tests from the my-project directory by simply running pytest:
$ pytest
========================================== test session starts ==========================================
platform linux -- Python 3.11.1, pytest-7.2.0, pluggy-1.0.0
rootdir: /home/lars/tmp/python/my-project
collected 1 item
tests/test_script.py . [100%]
=========================================== 1 passed in 0.00s ===========================================
In this setup, the file src/conftest.py is what allows pytest to automatically discover the location of your application code. Alternatively, you could instead specify that in your pyproject.toml file like this:
[tool.pytest.ini_options]
pythonpath = [
"src"
]
pytest also works if you write unittest-style tests as you show in your question; the above behavior would be identical if tests/test_script.py looked like:
import unittest
import myproject.script
class TestScript(unittest.TestCase):
def test_double(self):
res = myproject.script.double(2)
self.assertEqual(res, 4)
if __name__ == '__main__':
unittest.main()
(But I prefer pytest's simpler syntax.)
Possibly useful reading:
Good integration practices
If you really want to use unittest you can use the same directory layout, but you will need to update sys.path for it to run correctly. The easiest way to do that is:
PYTHONPATH=$PWD/src python -m unittest
This will automatically discover all your tests and run them. You can run a single test like this:
PYTHONPATH=$PWD/src python -m tests.test_script
You can avoid needing to modify sys.path if you use a simpler directory layout:
my-project/
├── myproject
│   ├── __init__.py
│   └── script.py
└── tests
├── __init__.py
└── test_script.py
Both pytest and python -m unittest, when run from the my-project directory, will correctly discover and run your tests without requiring any path modifications.

pytest error when importing my function decorator

I'm creating a pytest unit test for a function in my software.
Before even starting to test, pyunit seems to be unable to import my "cache_offline" decorator which is indirectly imported when I import the function I'm testing in my test.
I'm using Anaconda embedding Python 3.7 and pytest 5.2.2
I tried to comment out the code where the decorator is applied to my functions, when I do so the pytest error disappear and the tests execute properly.
My test is in ./tests/scripts/test_scripts_helper.py and I run pytest at the project root .
Pytest finds properly my test (see the error message), so this is not the problem at hand here.
My test imports and wants to test the function read_tiff_tag from a package vorace.scripts_helper, which imports a function safe_mkdir from package vorace.core.misc, which imports the package vorace.core.vorace, in which 3 functions are decorated with the decorator cache_offline from package vorace.core.misc
I tried both running the tests using either py.test or python -m pytest at the root of my project.
My project have the following structure (simplified).
The code root is ./vorace
The tests root is ./tests
.
├── conftest.py
├── tests
│   ├── __init__.py
│   ├── scripts
│   │   ├── __init__.py
│   │   └── test_scripts_helper.py
│   └── tests_data
│   └── test_ROI.tif
└── vorace
├── __init__.py
├── core
│   ├── __init__.py
│   ├── misc.py
│   └── vorace.py
└── scripts
├── __init__.py
   ├── batch_analyzis.py
   └── scripts_helper.py
I tried :
with and without empty __init__.py in each subfolder of the tests folder. -> no change
with and without an empty conftest.py at the root of the project. -> no change
executing a test which doesn't need any import in my test_scripts_helper.py file (with my test causing the problem being commented out) -> the test executes properly
I suspect kind of a circular import problem but I've always been told that it can't happen in python. Maybe the decorators are an exception to this rule ?
My vorace.core.misc code, with the decorator
from vorace.core import vorace
[...]
def cache_offline(cache_path=os.getcwd()):
[...]
def decorator(func):
[...]
def wrapper(*args, **kwargs):
[...]
return result
return wrapper
return decorator
def safe_mkdir(path):
[...]
One of the decorated functions in vorace.core.vorace
from vorace.core.misc import *
[...]
#cache_offline(cache_path=".cache")
def classify_clusters_by_connectivity(xyz_data):
[...]
[...]
The output from executing py.test in the project root
==================== test session starts ====================
platform linux -- Python 3.7.3, pytest-5.2.2, py-1.8.0, pluggy-0.12.0
rootdir: /home/flo/PycharmProjects/VorAce
plugins: arraydiff-0.3, openfiles-0.3.2, doctestplus-0.3.0, remotedata-0.3.1
collected 0 items / 1 errors
==================== ERRORS ====================
_________ ERROR collecting tests/scripts/test_scripts_helper.py _________
tests/scripts/test_scripts_helper.py:1: in <module>
import vorace.scripts.scripts_helper as sh
vorace/scripts/scripts_helper.py:6: in <module>
from vorace.core.misc import safe_mkdir
vorace/core/misc.py:8: in <module>
from vorace.core import vorace
vorace/core/vorace.py:91: in <module>
#cache_offline(cache_path=".cache")
E NameError: name 'cache_offline' is not defined
If I execute a simple 0 == 0 test in my tests/scripts/test_scripts_helper.py file without importing from my project, the test runs with success.
When you run ./tests/scripts/test_scripts_helper.py python automatically sets ./tests/scripts/ into PYTHONPATH but not sets any other directories, so all your imports from other custom files should fail.
Set all your working directories into PYTHONPATH environment variable.
Something like that on Linux shell.
PYTHONPATH="${PYTHONPATH}:$dir
EDIT: I finally got the confirmation of a circular import.
At the opposite of what I thought, importing particular names from a module
like in from x import y can be sensitive to circular imports, where import x can't.
To solve it, I just imported the module and use the syntax using the module prefixing the function call.
More information here : https://www.reddit.com/r/Python/comments/51hdup/from_import_vs_import_on_circular_import/
The problem what finally not related specifically to the decorator or pytest.
I worked around this issue by putting my cache_offline decorator in a separated package vorace.core.caching.py. Now I import this package only from vorace.core.vorace where the function needing to be decorated resides.
This way my decorator is artificially excluded from the code imported by my unit test, but still available to the rest of my code for a normal application execution.
However pytest still have an issue here, it should not fail to import. I'm still taking any answer that can explain why pytest fails to import my decorator, and I keep the resolve for an answer of this kind.

Recursive unittest discover

I have a package with a directory "tests" in which I'm storing my unit tests. My package looks like:
.
├── LICENSE
├── models
│   └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│   ├── db
│   │   └── test_employee.py
│   └── test_tc.py
└── todo.txt
From my package directory, I want to be able to find both tests/test_tc.py and tests/db/test_employee.py. I'd prefer not to have to install a third-party library (nose or etc) or have to manually build a TestSuite to run this in.
Surely there's a way to tell unittest discover not to stop looking once it's found a test? python -m unittest discover -s tests will find tests/test_tc.py and python -m unittest discover -s tests/db will find tests/db/test_employee.py. Isn't there a way to find both?
In doing a bit of digging, it seems that as long as deeper modules remain importable, they'll be discovered via python -m unittest discover. The solution, then, was simply to add a __init__.py file to each directory to make them packages.
.
├── LICENSE
├── models
│   └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│   ├── db
│   │   ├── __init__.py # NEW
│   │   └── test_employee.py
│   ├── __init__.py # NEW
│   └── test_tc.py
└── todo.txt
So long as each directory has an __init__.py, python -m unittest discover can import the relevant test_* module.
If you're okay with adding a __init__.py file inside tests, you can put a load_tests function there that will handle discovery for you.
If a test package name (directory with __init__.py) matches the
pattern then the package will be checked for a 'load_tests' function. If
this exists then it will be called with loader, tests, pattern.
If load_tests exists then discovery does not recurse into the package,
load_tests is responsible for loading all tests in the package.
I'm far from confident that this is the best way, but one way to write that function would be:
import os
import pkgutil
import inspect
import unittest
# Add *all* subdirectories to this module's path
__path__ = [x[0] for x in os.walk(os.path.dirname(__file__))]
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for memname, memobj in inspect.getmembers(mod):
if inspect.isclass(memobj):
if issubclass(memobj, unittest.TestCase):
print("Found TestCase: {}".format(memobj))
for test in loader.loadTestsFromTestCase(memobj):
print(" Found Test: {}".format(test))
suite.addTest(test)
print("=" * 70)
return suite
Pretty ugly, I agree.
First you add all subdirectories to the test packages's path (Docs).
Then, you use pkgutil to walk the path, looking for packages or modules.
When it finds one, it then checks the module members to see whether they're classes, and if they're classes, whether they're subclasses of unittest.TestCase. If they are, the tests inside the classes are loaded into the test suite.
So now, from inside your project root, you can type
python -m unittest discover -p tests
Using the -p pattern switch. If all goes well, you'll see what I saw, which is something like:
Found TestCase: <class 'test_tc.TestCase'>
Found Test: testBar (test_tc.TestCase)
Found Test: testFoo (test_tc.TestCase)
Found TestCase: <class 'test_employee.TestCase'>
Found Test: testBar (test_employee.TestCase)
Found Test: testFoo (test_employee.TestCase)
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Which is what was expected, each of my two example files contained two tests, testFoo and testBar each.
Edit: After some more digging, it looks like you could specify this function as:
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for test in loader.loadTestsFromModule(mod):
print("Found Tests: {}".format(test._tests))
suite.addTests(test)
This uses the loader.loadTestsFromModule() method instead of the loader.loadTestsFromTestCase() method I used above. It still modifies the tests package path and walks it looking for modules, which I think is the key here.
The output looks a bit different now, since we're adding a found testsuite at a time to our main testsuite suite:
python -m unittest discover -p tests
Found Tests: [<test_tc.TestCase testMethod=testBar>, <test_tc.TestCase testMethod=testFoo>]
Found Tests: [<test_employee.TestCase testMethod=testBar>, <test_employee.TestCase testMethod=testFoo>]
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
But we still get the 4 tests we expected, in both classes, in both subdirectories.
The point using init.py, is that one may encounters side effects, like file not being the script file path. Using FOR DOS command can help (not found of DOS commands, but sometimes it helps
setlocal
set CWD=%CD%
FOR /R %%T in (*_test.py) do (
CD %%~pT
python %%T
)
CD %CWD%
endlocal
/R allows for walkthrough the hierarchy from current folder.
(expr) allows for selecting test files (I use _test.py)
%%~pT is $(dirname $T) in shell.
I saved and restore my original directory, as the .bat leaves me where it ends
setlocal ... endlocal to not pollute my environment with CWD.

python - Nose not discovering package level tests in Django

I'm setting up a directory structure for my Django app to separate functional and unit tests. I am using nose as the test runner for my Django project.
At the root of the Django project, I have a folder called "tests" that has this structure:
tests
├── __init__.py
├── functional
│ ├── __init__.py
└── unit
├── __init__.py
├── data.py
├── tests.py
If I want to run just the unit tests, should I not be able to use the following from the project root:
$ nosetests tests.unit
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
As you can see, this doesn't find the tests in the tests.py file.
However, when I run using the directory structure, the tests are found as they should be:
$ nosetests tests/unit/
E
# .. Some errors I expected because settings are not initialized when called this way
-----------------
Ran 1 test in 0.001s
FAILED (errors=1)
What am I missing? My main issue is that I have a setup function in tests.unit.__init__.py that should be called for creating the data in the test DB for the upcoming tests.
Thanks
This all depends on what kind of code is in tests/unit/__init__.py
When you say
nosetests tests.unit
You are pointing to unit/__init__.py not the directory unit/ thus if you had no tests in your __init__.py module then nothing would be run. So it is understandable when you say you used the directory path and then your tests started working.
You mention
What am I missing? My main issue is that I have a setup function in
tests.unit.init.py that should be called for creating the data in
the test DB for the upcoming tests.
It is likely that although you have a setup function in __init__.py you may have not ever imported your test functions into __init__.py
One quick fix to this would be to add this line in __init__.py
from tests.unit.tests import *
That said it is really not very wise to be putting any code in __init__.py at all and if you have code that returns some kind of configuration data I would recommend creating a new library module with functions that will return configuration data to your tests

Good way to get file locations when the location of the python script looking for the file can change?

I have a folder 'pyMS' that contains scripts with doctests. Also in pyMS is a folder 'test' which has both unit tests and a folder 'testfiles' that contains files with data that I use for testing.
pyMS/
├── __init__.py
├── baseFunctions.py
└── test
├── __init__.py
├── test_baseFunctions.py
└── testfiles
└── testfile.txt
I want to use the unittest to run the doctest with
suite.addTest(doctest.DocTestSuite("baseFunctions")
However, I'm running in the problem when I try to use files in the doctest.
This would work when doing the doctest from the commandline (python baseFunctions -v):
testfile = open('test/testfiles/testfile.txt')
but when running it from the unittest it gives an IOError because the test_baseFunctions.py is in the test folder, so the correct location for the file is /testfiles/testfile.txt instead of test/testfiles/testfile.txt.
My very ugly solution now is
import os
testfile = open(os.abspath('').split('pyMS')[0]+'pyMS'+os.sep+'test'+os.sep+'testfiles'+os.sep+'testfile.txt'
What would be a better way to handle this?
You can standardize by doing an os.chdir() to the appropriate place. For running tests, I can't think of any reason not to.

Categories