It is pytest plugin that helps You to test applications written with Google's AppEngine.


  • --with-gae: Turns on this plugin
  • --gae-path: AppEngine's root (default google_appengine)
  • --gae-project-path: Your project's root (default ./)


Plugin does not prevent You from using code/modules that AppEngine's environment refuse to execute. So, You can easily do something like that:

import socket
import numpy

And tests just pass. But You can not run this code on AppEngine environment, because of sandboxing. See: AppEngine Docs

This plugin uses internal AppEngine's code and there is no guarantee that Google is not going to change it.


This project was inspired by nose-gae plugin for nose

Usage example

Let assume we have a directory that looks something like that

├── gae               # AppEngine's root
│   ├── ...
├── src               # Your project's root
│   ├── app.yaml
│   ├── index.yaml
│   └──
└── tests             # Tests' dir

#!/usr/bin/env python
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp.util import login_required
from google.appengine.api import users
from google.appengine.ext import db

class MyModel(db.Model):
    my_field = db.StringProperty(required=False)

class IndexHandler(webapp.RequestHandler):
    def get(self):

class UsersHandler(webapp.RequestHandler):

    def get(self):
        if users.is_current_user_admin():

def make_application():
    return webapp.WSGIApplication([('/index', IndexHandler),
                                   ('/users', UsersHandler)], debug=True)

def main():
    application = make_application()

if __name__ == '__main__':

Testing models

from google.appengine.ext import db
import pytest

from main import MyModel

def test_basic():
    m = MyModel(my_field='Foo')
    assert 'Foo' == m.my_field

def test_new_model():
    m = MyModel(my_field='Foo')
    pytest.raises(db.NotSavedError, lambda: m.key())

def test_saved_model():
    m = MyModel(my_field='Foo')
    assert m.key()

Using with WebTest

We could test our handlers with the help of WebTest library.

We would create three funcargs' functions that allows us to test application:

  • From anonymous user perspective
  • From authorized user perspective
  • From admin perspective

We could do that by altering os.enviroment

import os

from webtest import TestApp
from main import make_application

def pytest_funcarg__anon_app(request):
    os.environ.update({'USER_EMAIL': '',
                        'USER_ID': '',
                        'AUTH_DOMAIN': 'google',
                        'USER_IS_ADMIN': '0'})
    return TestApp(make_application())

def pytest_funcarg__user_app(request):
    os.environ.update({'USER_EMAIL': '',
                       'USER_ID': '1',
                       'AUTH_DOMAIN': 'google',
                       'USER_IS_ADMIN': '0'})
    return TestApp(make_application())

def pytest_funcarg__admin_app(request):
    os.environ.update({'USER_EMAIL': '',
                       'USER_ID': '2',
                       'AUTH_DOMAIN': 'google',
                       'USER_IS_ADMIN': '1'})
    return TestApp(make_application())

def test_index(anon_app):
    assert "Index" in anon_app.get('/index')

def test_user_with_user(user_app):
    assert "User" in user_app.get('/users')

def test_user_with_anon(anon_app):
    assert '302 Moved Temporarily' == anon_app.get('/users').status

def test_user_with_admin(admin_app):
    assert "Admin" in admin_app.get('/users')


py.test tests --with-gae --gae-path=gae --gae-project-path=./src/ :
platform linux2 -- Python 2.5.5 -- pytest-2.0.0
collected 7 items

tests/ ....
tests/ ...

============ 7 passed in 0.64 seconds ============