Google App Engine用フレームワーク Kay のバージョン 0.10.0 RC1が公開されています。
Kay 0.10.0 RC1でユニットテストを行う方法を紹介します。
(1) unittest.TestCase から GAETestBase への変更
テストを行うためのフレームワークが unittest.TestCase から GAETestBase へ変更されました。
GAETestBaseの導入によって、コマンドラインによるテスト以外にもブラウザで「http://localhost:8080/_ah/test」にアクセスすることでテストを実行できるようになりました。
また、ローカル環境だけでなく本番環境でテストすることができます。
参考:GoogleAppEngineの強力なテストツールGAETestBaseがKayに標準添付された
以前の方法
import unittest
class ModelTest(unittest.TestCase):
…
新しい方法
from kay.ext.testutils.gae_test_base import GAETestBase
class ModelTest(GAETestBase):
…
(2) CLIでビューのテストを行うときは、「manage.py preparse_apps」が必要
RC1ではCLIでビューのテストを行うときは、「manage.py preparse_apps」が必要です。
「manage.py preparse_apps」を実行しないと、「TemplateNotFound」のエラーになります。
python manage.py preparse_apps
python manage.py test
正式版では修正されている可能性があります。
(3) KIND_NAME_UNSWAPPEDをTrueにする
unittestで正常に動作していたテストがGAETestCaseに変更して動かなくなった場合、「KIND_NAME_UNSWAPPED」属性をTrueにすると、動作する可能性が高いです。
GAETestCaseではデフォルトでkindを入れ替えます。
KIND_NAME_UNSWAPPEDをTrueにすることで、この処理を行いません。
class MyappTestCase(GAETestBase):
KIND_NAME_UNSWAPPED = True
正式版では修正されている可能性があります。
(4) CLIによるテストでdb_hookを有効にする
以下のコードのようにsetUp()でフックを登録することで、CLIでテストしたときもdb_hookが機能しました。
class ModelTest(GAETestBase):
KIND_NAME_UNSWAPPED = True
def setUp(self):
from google.appengine.api import apiproxy_stub_map
from kay.utils.db_hook import post_hook
from kay.utils.db_hook import pre_hook
apiproxy_stub_map.apiproxy.GetPostCallHooks().Clear()
apiproxy_stub_map.apiproxy.GetPreCallHooks().Clear()
apiproxy_stub_map.apiproxy.GetPostCallHooks().Append('post_hook', post_hook, 'datastore_v3')
apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('pre_hook', pre_hook, 'datastore_v3')
以下は、Kay 0.10.0 RC1でチュートリアルのプロジェクトをテストするコードです。
# -*- coding: utf-8 -*-
#import unittest
from google.appengine.ext import db
from werkzeug import BaseResponse, Client
from kay.app import get_application
from kay.utils.test import (
init_recording, get_last_context, get_last_template,
disable_recording,
)
from myapp.models import (Comment, Category)
from kay.ext.testutils.gae_test_base import GAETestBase
#class ModelTest(unittest.TestCase):
class ModelTest(GAETestBase):
KIND_NAME_UNSWAPPED = True
def setUp(self):
from google.appengine.api import apiproxy_stub_map
from kay.utils.db_hook import post_hook
from kay.utils.db_hook import pre_hook
apiproxy_stub_map.apiproxy.GetPostCallHooks().Clear()
apiproxy_stub_map.apiproxy.GetPreCallHooks().Clear()
apiproxy_stub_map.apiproxy.GetPostCallHooks().Append('post_hook', post_hook, 'datastore_v3')
apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('pre_hook', pre_hook, 'datastore_v3')
def tearDown(self):
db.delete(Comment.all().fetch(10))
def test_model(self):
c = Comment(body=u'Hello Test!')
c.put()
comments = Comment.all().fetch(100)
self.assertEquals(len(comments), 1)
self.assertEquals(comments[0].body, 'Hello Test!')
def test_db_hook(self):
category = Category(name='Category')
category.put()
self.assertEquals(Category.all().count(100), 1)
Comment(body='Body', category=category).put()
self.assertEquals(Comment.all().count(100), 1)
db.delete(category)
#カテゴリーの削除に成功した
self.assertEquals(Category.all().count(100), 0)
#db_hookによりコメントをも削除されている
self.assertEquals(Comment.all().count(100), 0)
#class MyappTestCase(unittest.TestCase):
class MyappTestCase(GAETestBase):
KIND_NAME_UNSWAPPED = True
def setUp(self):
init_recording()
app = get_application()
self.client = Client(app, BaseResponse)
def tearDown(self):
db.delete(Category.all().fetch(10))
db.delete(Comment.all().fetch(10))
disable_recording()
def test_post(self):
response = self.client.get('/')
used_template = get_last_template()
used_context = get_last_context()
csrf_token = used_context['form'].csrf_token
response = self.client.post('/', data={'body': 'Hello',
'_csrf_token': csrf_token},
follow_redirects=True)
comments = Comment.all().fetch(100)
self.assertEquals(len(comments), 1)
def test_db_hook(self):
category = Category(name='Category')
category.put()
self.assertEquals(Category.all().count(100), 1)
Comment(body='Body', category=category).put()
self.assertEquals(Comment.all().count(100), 1)
url = '/category/delete/%s' % category.key()
response = self.client.get(url)
self.assertEquals(response.status_code, 302)
#カテゴリーの削除に成功した
self.assertEquals(Category.all().count(100), 0)
#db_hookによりコメントをも削除されている
self.assertEquals(Comment.all().count(100), 0)
2010年6月6日追記
オンラインマニュアルのテストの項目がGAETestBaseを使ったものに更新されていました。
17. Test の実施 — Kay v0.10.0 documentation
Pingback: Google App Engine用フレームワークKayでfeed配信する方法 | 山本隆の開発日誌