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)

Reply via email to