Automated testing of nested packages - unit-testing

I am sorry for the dumb question, but I have been banging my head against a wall for past two hours.
I want to use relative imports and my project structure looks like this:
auto_testing
+ tests
+ __init__.py
+ my_module.py
+ src
+ __init__.py
+ my_module.py
+ __init__.py
The contents of tests/my_module.py are:
import unittest
from src.my_module import MyClass
class TestMyClass(unittest.TestCase):
def setUp(self):
self.inst = MyClass()
def test_division_by_zero(self):
self.assertRaises(ZeroDivisionError, self.inst.divide, 1, 0)
def run_tests():
unittest.main()
if __name__ == '__main__':
run_tests()
Then I run commands in terminal
cd auto_testing
python3 -m tests.my_module
and get Error while finding spec for 'tests.my_module.py' (<class 'AttributeError'>: 'module' object has no attribute '__path__').
When I run
cd auto_testing
python3
import tests.my_module
tests.my_module.run_tests()
I get Ran 0 tests (why no tests were found should be another question, but the point is, that MyClass is imported properly and unittest.main() seems to be called; checked with print).
Should I go sleeping for I missed something very simple?

I should have went sleeping.
The commands I needed to run were:
cd auto_testing
python3 -m unittest tests/my_module.py
This also runs my tests.

Related

Python script on Django shell not seeing import if import not set as global?

I have searched the stackoverflow and wasn't able to find this. I have noticed something I can not wrap my head around. When run as normal python script import works ok, but when run from Django shell it behaves weird, needs to set import as global to be seen.
You can reproduce it like this. Make a file test.py in folder with manage.py. Code you can test with is this.
This doesn't work, code of test.py:
#!/usr/bin/env python3
import chardet
class LoadList():
def __init__(self):
self.email_list_path = '/home/omer/test.csv'
#staticmethod
def check_file_encoding(file_to_check):
encoding = chardet.detect(open(file_to_check, "rb").read())
return encoding
def get_encoding(self):
return self.check_file_encoding(self.email_list_path)['encoding']
print(LoadList().get_encoding())
This works ok when chardet set as global inside test.py file:
#!/usr/bin/env python3
import chardet
class LoadList():
def __init__(self):
self.email_list_path = '/home/omer/test.csv'
#staticmethod
def check_file_encoding(file_to_check):
global chardet
encoding = chardet.detect(open(file_to_check, "rb").read())
return encoding
def get_encoding(self):
return self.check_file_encoding(self.email_list_path)['encoding']
print(LoadList().get_encoding())
First run is without global chardet and you can see the error. Second run is with global chardet set and you can see it works ok.
What is going on and can someone explain this to me? Why it isn't seen until set as global?
Piping a file into shell is the same as piping it into the python command. It's not the same as running the file with python test.py. I suspect it's something to do with the way the the newlines are interpreted as to how the file is really parsed, but don't have time to check.
Instead of this approach I'd recommend you write a custom management command.

Test different invocation pattern

I want to test how a project generated by cookiecutter behave with multiple invocation patterns.
Given the following generated project
proj/
proj/
__init__.py
__main__.py
Content of __init__.py:
def func():
pass
Content of __main__.py:
from proj import func
def main():
func()
if __name__ == '__main__':
main()
(I have read about issues of __main__.__spec__, this is not what this question is about. In fact, I would like to test-drive -TDD- my work arounds of these issues)
Now I write tests in which the generated project structure and location is known (available via pytest fixtures, for instance)
test_run_proj_script():
"""Test behavior of ``python3 ./proj``"""
pass # how to run this?
test_run_proj_module():
"""Test behavior of ``python3 -m ./proj``"""
pass # how to run this?
Bonus: inject values for sys.argv ?

uwsgi and flask - cannot import name "appl"

I created several servers, without any issue, with the stack nginx - uwsgi - flask using virtualenv.
with the current one uwsgi is throwing the error cannot import name "appl"
here is the myapp directory structure:
/srv/www/myapp
+ run.py
+ venv/ # virtualenv
+ myapp/
+ init.py
+ other modules/
+ logs/
here is the /etc/uwsgi/apps-avaliable/myapp.ini
[uwsgi]
# Variables
base = /srv/www/myapp
app = run
# Generic Config
# plugins = http, python
# plugins = python
home = %(base)/venv
pythonpath = %(base)
socket = /tmp/%n.sock
module = %(app)
callable = appl
logto = %(base)/logs/uwsgi_%n.log
and this is run.py
#!/usr/bin/env python
from myapp import appl
if __name__ == '__main__':
DEBUG = True if appl.config['DEBUG'] else False
appl.run(debug=DEBUG)
appl is defined in myapp/ _ init _ .py as an instance of Flask()
(underscores spaced just to prevent SO to turn them into bold)
I accurately checked the python code and indeed if I activate manually the virtualenvironment and execute run.py manually everything works like a charm, but uwsgi keeps throwing the import error.
Any suggestion what should I search more ?
fixed it, it was just a read permissions issue. The whole python app was readable by my user but not by the group, therefore uwsgi could not find it.
This was a bit tricky because I deployed successfully many time with the same script and never had permissions issues

After installing lpthw.web the does nothing

So, I am going over "Learn Python The Hard Way" and have an issue with Chapter 50 "Building my first website".
jharvard#appliance (~/Dropbox/Python/gothonweb): ls -R
bin docs gothonweb templates tests
./bin:
app.py
./docs:
./gothonweb:
__init__.py
./templates:
./tests:
__init__.py
Trying to run app.py file with command: $python bin/app.py It supposed to start the server but it does not do anything at all. It just return to a prompt again.
#app.py
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
greeting = "Hello world"
return greeting
if __name__ == "__main__":
app.run
I installed lpthw using pip first.
$pip install lpthw.web
When I run the file it gave me ImportError: no 'web' exists
So I installed webpy myself using with help of http://webpy.org/install
And now I'm getting no result at all.
Python I am using is Python 2.7.8 : Anaconda 2.1.0 . So there must be no conflict. Any suggestions? Thank you.
So, I fixed it successfully but adding parenthesis to the function app.ru()
#app.py
....
greeting = "Hello world"
return greeting
if __name__ == "__main__":
app.run

Configure Django to find all doctests in all modules?

If I run the following command:
>python manage.py test
Django looks at tests.py in my application, and runs any doctests or unit tests in that file. It also looks at the __ test __ dictionary for extra tests to run. So I can link doctests from other modules like so:
#tests.py
from myapp.module1 import _function1, _function2
__test__ = {
"_function1": _function1,
"_function2": _function2
}
If I want to include more doctests, is there an easier way than enumerating them all in this dictionary? Ideally, I just want to have Django find all doctests in all modules in the myapp application.
Is there some kind of reflection hack that would get me where I want to be?
I solved this for myself a while ago:
apps = settings.INSTALLED_APPS
for app in apps:
try:
a = app + '.test'
__import__(a)
m = sys.modules[a]
except ImportError: #no test jobs for this module, continue to next one
continue
#run your test using the imported module m
This allowed me to put per-module tests in their own test.py file, so they didn't get mixed up with the rest of my application code. It would be easy to modify this to just look for doc tests in each of your modules and run them if it found them.
Use django-nose since nose automatically find all tests recursivelly.
Here're key elements of solution:
tests.py:
def find_modules(package):
"""Return list of imported modules from given package"""
files = [re.sub('\.py$', '', f) for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py") and os.path.basename(f) not in ('__init__.py', 'test.py')]
return [imp.load_module(file, *imp.find_module(file, package.__path__)) for file in files]
def suite(package=None):
"""Assemble test suite for Django default test loader"""
if not package: package = myapp.tests # Default argument required for Django test runner
return unittest.TestSuite([doctest.DocTestSuite(m) for m in find_modules(package)])
To add recursion use os.walk() to traverse module tree and find python packages.
Thanks to Alex and Paul. This is what I came up with:
# tests.py
import sys, settings, re, os, doctest, unittest, imp
# import your base Django project
import myapp
# Django already runs these, don't include them again
ALREADY_RUN = ['tests.py', 'models.py']
def find_untested_modules(package):
""" Gets all modules not already included in Django's test suite """
files = [re.sub('\.py$', '', f)
for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py")
and os.path.basename(f) not in ALREADY_RUN]
return [imp.load_module(file, *imp.find_module(file, package.__path__))
for file in files]
def modules_callables(module):
return [m for m in dir(module) if callable(getattr(module, m))]
def has_doctest(docstring):
return ">>>" in docstring
__test__ = {}
for module in find_untested_modules(myapp.module1):
for method in modules_callables(module):
docstring = str(getattr(module, method).__doc__)
if has_doctest(docstring):
print "Found doctest(s) " + module.__name__ + "." + method
# import the method itself, so doctest can find it
_temp = __import__(module.__name__, globals(), locals(), [method])
locals()[method] = getattr(_temp, method)
# Django looks in __test__ for doctests to run
__test__[method] = getattr(module, method)
I'm not up to speed on Djano's testing, but as I understand it uses automatic unittest discovery, just like python -m unittest discover and Nose.
If so, just put the following file somewhere the discovery will find it (usually just a matter of naming it test_doctest.py or similar).
Change your_package to the package to test. All modules (including subpackages) will be doctested.
import doctest
import pkgutil
import your_package as root_package
def load_tests(loader, tests, ignore):
modules = pkgutil.walk_packages(root_package.__path__, root_package.__name__ + '.')
for _, module_name, _ in modules:
try:
suite = doctest.DocTestSuite(module_name)
except ValueError:
# Presumably a "no docstrings" error. That's OK.
pass
else:
tests.addTests(suite)
return tests