2014/06/18

AppEngine(Python)でDatastoreのユニットテストする方法

AppEngineはプロダクションサーバ(いわゆる運用サーバ)上でDatastoreなどを用いたのテストができません。データを完璧に切り分けて自由にいじり倒してテストできるような環境を作ってくれたりすると嬉しいのですが、そういう感じには進んでいないようです。
ということで、似たような環境をローカルに構築してテストをするしかありません。
今回はその方法をメモしておきます。

STEP1: Datastoreのデータをローカルに構築
前回のエントリーを参考にしてAppEngine上で運用されているDatastoreをダウンロードして、ローカルの開発サーバにアップロードします。一度アップロードしておけば、次からは既にデータが構築された状態になっているので、構築作業は初めの1回だけです。

STEP2(a): Unitテストを記述する
AppEngine/pでUnitテストを書く方法は簡単です。unittest.TestCaseクラスを継承するだけ。
import unittest
from entity import ShopData

class TestShopData(unittest.TestCase):
    def setUp(self):
        #do something before testing each test method

    def tearDown(self):
        # do something after testing each test method

    def test_check_name(self):
        shop = ShopData.all().fileter('email =', 'adamrocker@gmail.com').get()
        self.assertEqual(shop.name, 'adam')

STEP2(b): サンドボックス環境でUnitテストする
AppEngineのUnitテストにはtestbedというサンドボックス環境が提供されています。
testbedを簡単に説明するなら、各テストで副作用の有るような処理(例えばmemcacheの登録・削除など)を各テストが終了した時になかった事にしてくれる便利なテスト環境です。
testbedを使うには以下のようにsetUpで準備しtearDownで後処理を加えます。

import unittest
from google.appengine.ext import testbed
from entity import ShopData

class TestShopData(unittest.TestCase):
    def setUp(self):
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub(datastore_file='/tmp/gae.datastore', use_sqlite=True)

    def tearDown(self):
        self.testbed.deactivate()

    def test_put_shop(self):
        shop = ShopData()
        shop.email = 'eve@gmail.com'
        shop.name = 'eve'
        shop.put()
        count = ShopData().all().filter('email =', 'eve@gmail.com').count()
        self.assertEqual(count, 1)

init_datastore_v3_stubメソッドの引数datastore_fileでローカルの開発サーバが保持しているDatastoreのファイルの位置を示しています。
開発サーバを起動するときに指定したオプション(--datastore_path)のファイルパスです。datastore_file引数を持たない場合は何も格納されていないDatastoreがメモリ上に作られます。既存のDatastoreを用いてテストしたい場合はdatastore_fileを利用するとよいでしょう。
UnitテストでShopDataを追加していますが、setUpで設定したtestbedがコレを受け止め、test_put_shopメソッドが終了するとtearDownメソッドでtestbedがdeactivateし、Datastoreの変更がなかったことになります。
なので、test_put_shopテストを何度実行してもTrueになります。