Kay 0.10.0 RC1でユニットテストを行う方法

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

コメント

  1. Pingback: Google App Engine用フレームワークKayでfeed配信する方法 | 山本隆の開発日誌

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください