attached is a controller that i use, adapted from the testing tools in the
admin interface. note that i set a request parameter 'test_db' and in
models i detect that parameter and connect to a different namespace so that
my tests run in a controlled DB environment - remember that if you want to
setup some test data to start with. :)
i have not used this in a live GAE environment so i don't know how well it
works. i fear that extensive tests would run past the 60 second timeout
for web requests.
let me know if you have questions.
On Tuesday, March 20, 2012 1:54:03 PM UTC-7, David Phillips wrote:
>
> I am writing a web application using web2py for execution on app
> engine. I've deployed other web2py apps there but I've never used any
> app engine-specific utilities. In this project I want to use app
> engine's taskqueue and run it on a backend instance.
>
> I'm developing on app engine's dev_appserver which is working out
> okay, but but I'm not seeing how to do unit testing.
>
> Is this a problem that has been solved already?
"""
This controller has methods to run the doctests in web2py. app.yaml should
be configured so that only google app engine administrators have access to
these methods
"""
import sys
def die(msg):
print >> sys.stderr, msg
sys.exit(1)
@auth.requires_membership('admin-general')
def controllers():
"""
a method to test doctests
>>> #no doctests since that will ruin the tests
>>> assert(True==True)
"""
from gluon.fileutils import listdir
from gluon.admin import apath
app = request.application
if len(request.args) > 0:
file = request.args[0]
else:
file = '.*\.py'
if request.vars.reset:
#set to test namespace
from google.appengine.api import namespace_manager
namespace_manager.set_namespace('rockriver_test')
rows = db(db.end_user.id>0).select()
for table in db.tables:
db(db[table].id>0).delete()
create_test_data()
controllers = listdir(apath('%s/controllers/' % app, r=request), file + '$')
return dict(app=app, controllers=controllers)
@auth.requires_membership('admin-general')
def modules():
"""
Run doctests in web2py environment. runs the tests in the modules
>>> #no doctests since that will ruin the tests
>>> assert(True==True)
"""
from gluon.shell import parse_path_info, env
import glob
import types
import sys
import cStringIO
import doctest
import os
verbose=request.vars.verbose or False
testpath=request.application
import_models=True
#get a list of the modules to test
cdir = os.path.join('applications', testpath, 'modules')
if not os.path.isdir(cdir):
die("modules is not a directory")
files = glob.glob(os.path.join(cdir, '*.py'))
#save stdout so we can capture data and reset it.
stdout = sys.stdout
html = ''
for testfile in files:
html += '<h2>Testing module "%s.py" ... done.</h2><br/>\n' % testfile
globs = env(testpath, import_models)
ignores = globs.keys()
execfile(testfile, globs)
def doctest_object(name, obj, html, new_env):
"""doctest obj and enclosed methods and classes."""
if type(obj) in (types.FunctionType, types.TypeType,
types.ClassType, types.MethodType,
types.UnboundMethodType):
# Reload environment before each test.
globs = env(testpath, c='default', f='test_modules',
import_models=import_models,
extra_request={'env':new_env, 'test_db':True})
execfile(testfile, globs)
number_doctests = sum([len(ds.examples) for ds in doctest.DocTestFinder().find(obj)])
if number_doctests>0:
sys.stdout = cStringIO.StringIO()
doctest.run_docstring_examples(obj, globs=globs,
name='%s: %s' % (os.path.basename(testfile),
name), verbose=verbose)
report = sys.stdout.getvalue().strip()
if report:
pf = 'failed'
else:
pf = 'passed'
html += '<h3 class="%s">Function %s [%s]</h3>\n' \
% (pf, name, pf)
if report:
html += CODE(report, language='web2py', \
link='/examples/global/vars/').xml()
html += '<br/>\n'
else:
html += \
'<h3 class="nodoctests">Function %s [no doctests]</h3><br/>\n' \
% (name)
#I don't think that the following is needed for modules.
#if we find that tests are not being executed, maybe turn
#this back on.
if type(obj) in (types.TypeType, types.ClassType):
for attr_name in dir(obj):
# Execute . operator so decorators are executed.
try:
o = eval('%s.%s' % (name, attr_name), globs)
except:
#that method does not exist, skip over it.
pass
else:
doctest_object(attr_name, o, html, new_env)
return html
for (name, obj) in globs.items():
if name not in ignores:
html = doctest_object(name, obj, html, Storage(globs))
sys.stdout = stdout
return dict(html=XML(html))
def unittests():
"""
run the unittests
>>> #no doctests since that will ruin the tests
>>> assert(True==True)
"""
import os
import sys
import glob
import cStringIO
from gluon.shell import env
#save stdout so we can capture data and reset it.
stdout = sys.stdout
stderr = sys.stderr
#get a list of the modules to test
cdir = os.path.join('applications', request.application, 'tests')
if not os.path.isdir(cdir):
die("applications/%s/tests is not a directory"%request.application)
files = glob.glob(os.path.join(cdir, '*.py'))
html = ''
test_count = 0
pass_count = 0
fail_count = 0
for testfile in files:
test_count += 1
html += '<h2>Running Test "%s.py" ... done.</h2><br/>\n' % testfile
# Reload environment before each test.
globs = env(request.application, c='default', f='index',
import_models=True, extra_request={'test_db':True})
sys.stdout = cStringIO.StringIO()
execfile(testfile, globs)
report = sys.stdout.getvalue().strip()
if report.find('FAIL') >= 0:
fail_count += 1
html += '<h3 class="failed">FAILED</h3>\n'
html += CODE(report, language='web2py', \
link='/examples/global/vars/').xml()
else:
pass_count += 1
html += '<h3 class="passed">PASSED</h3>\n'
sys.stdout = stdout
return dict(html=XML(html),
test_count = test_count,
pass_count = pass_count,
fail_count = fail_count)