Jaco du Plessis

How To Test a Python Google App Engine Project

python google app engine selenium

I've been working on an App Engine project for the past couple of months. This is my first (and probably last) time working with the GAE Python Standard Environment.

One of the things I found most frustrating about the environment is the lack of support for running functional tests against the GAE testbed. Because I'm used to Django's LiveServerTestCase, it seemed impossible to me that there is no solution for this.

I suppose that when it was popular to run apps on GAE Python SE, it was before the time people added critical functionality to their app via Javascript.

Well, eventually I found it. The secret ingredient lies within the http module in the Webtest library.

We create two mixins to use with the test. The first sets up the testbed, and the second starts the app server in a different thread.

from google.appengine.ext import testbed
from webtest import http
from main import app  # your WSGI app

class TestBedMixin(object):
    def setUp(self):
        super(TestBedMixin, self).setUp()
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_user_stub()
        self.testbed.init_taskqueue_stub()
        # add other stubs you may need

    def tearDown(self):
        super(TestBedMixin, self).tearDown()
        self.testbed.deactivate()


class LiveServerMixin(object):
    def setUp(self):
        super(LiveServerMixin, self).setUp()
        wsgi = WhiteNoise(app, root=static_path, prefix='static/')
        self.server = http.StopableWSGIServer.create(wsgi)
        self.url = self.server.application_url[:-1]  # remove trailing slash

    def tearDown(self):
        super(LiveServerMixin, self).tearDown()
        self.server.shutdown()

Now we can write our test:

from selenium import webdriver
from models import MyModel

class TestSomething(TestBedMixin, LiveServerMixin, unittest.TestCase):
    def test_some_thing(self):

        driver = webdriver.Chrome()
        driver.get(self.url)

        # do stuff with selenium
        # ...

        # check that something was created in the datastore
        created = MyModel.query().count() > 0
        self.assertTrue(created)

This has solved a lot of headache for me.

You'll have notice the wrapping of the app in a Whitenoise class – this is for serving static files. Because you're not using the dev_appserver.py with your app.yaml, you need to serve static files somehow. Check out Whitenoise for more details.

Have a comment? Email me at jaco@jacoduplessis.co.za.