webtest を使った Google App Engine アプリケーションのテスト

Google App Engine (以下 GAE)アプリケーションの、Web API 部分のテストを自動化するために、webtest というツールを導入してみた。

webtest は WSGI アプリケーションをテストするための便利ツール。WSGI アプリケーションにダミーのリクエストを簡単に送る事ができる。webtest は easy_install を使ってインストール。

GAE アプリケーションは、中で Datastore API や Memcached API を使っている場合がほとんど。そのため、テストではスタブを登録する必要がある。以前紹介した GAETestBase を継承してテストクラスを作るといい。

webtest を使ったテストコードのサンプルがこちら。パスの設定と GAETestBase クラスの定義は、別ファイルに書いて import した方が、後々使い回せていいかも。

#!/usr/bin/env python
#encoding: utf-8

"""
テストの下準備
GAE SDK のパスを設定したり、スタブを登録したりする
"""

import os
import sys

GAE_HOME = 'C:/google_appengine'
PROJECT_HOME = os.path.join(GAE_HOME, 'apps', 'greeting')

EXTRA_PATHS = [
    GAE_HOME,
    PROJECT_HOME,
    os.path.join(GAE_HOME, 'google', 'appengine', 'api'),
    os.path.join(GAE_HOME, 'google', 'appengine', 'ext'),
    os.path.join(GAE_HOME, 'lib', 'yaml', 'lib'),
    os.path.join(GAE_HOME, 'lib', 'webob'),
    os.path.join(GAE_HOME, 'lib', 'django'),
]

sys.path = EXTRA_PATHS + sys.path

import unittest
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
from google.appengine.api import mail_stub
from google.appengine.api import urlfetch_stub
from google.appengine.api import user_service_stub
from google.appengine.api import users
from google.appengine.ext import db

APP_ID = u"test_id"
AUTH_DOMAIN = "gmail.com"
LOGGED_IN_USER = "test@example.com"

class GAETestBase(unittest.TestCase):
    def setUp(self):
        # Regist API Proxy
        apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()

        # Regist Datastore stub
        stub = datastore_file_stub.DatastoreFileStub(APP_ID,
                '/dev/null',
                '/dev/null')
        apiproxy_stub_map.apiproxy.RegisterStub("datastore_v3", stub)
        os.environ["APPLICATION_ID"] = APP_ID

        # Regist UserService stub
        apiproxy_stub_map.apiproxy.RegisterStub("user",
                user_service_stub.UserServiceStub())
        os.environ["AUTH_DOMAIN"] = AUTH_DOMAIN
        os.environ["USER_EMAIL"] = LOGGED_IN_USER
        
        # Regist urlfetch stub
        apiproxy_stub_map.apiproxy.RegisterStub('urlfetch',
                urlfetch_stub.URLFetchServiceStub())
        
        # Regist MailService stub
        apiproxy_stub_map.apiproxy.RegisterStub('mail',
               mail_stub.MailServiceStub()) 

"""
webtest を使ったテストコードはここから
"""

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
import webtest

# テスト対象のクラス
class GreetingHandler(webapp.RequestHandler):
    def get(self):
        self.response.out.write("Hello")

    def post(self):
        name = self.request.get("name", None)
        self.response.out.write("Hello, %s" % name)

# webtest を使ってテスト用の WSGI アプリを作成
# 作成したアプリを使ってダミーのリクエストを送信する
app = webtest.TestApp(webapp.WSGIApplication([
    ('/hello', GreetingHandler),
    ], debug=True))

# スタブを使うかもしれないので GAETestBase を継承
class GreetingHandlerTest(GAETestBase):
    def test_get(self):
        resp = app.get('/hello')
        self.assertEqual('200 OK', resp.status)
        self.assertEqual('Hello', resp.body)

    def test_post(self):
        resp = app.post('/hello', { "name": "Keima" })
        self.assertEqual('Hello, Keima', resp.body)

if __name__ == '__main__':
    unittest.main()