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)