GAE+Django+GAEUnit (Part-II)
ということで、GAEUnitを使いながらGAE+DjangoでTDD開発していくメモ二回目。作るものはStatというエンティティにデータを書き込む・読み出すアプリを書いてみる、ということにする。
アプリケーション作成
なぜかDjangoはコントローラとモデルの組み合わせをアプリケーションと呼ぶらしい。アプリケーション名はstatにする。
python manage.py startapp stat
→statというディレクトリが生成される。中身は__init__.py, views.py, models.py
unit_tests.py
まずはStatというクラスの実体を生成し、そいつがNoneではないことを確認。
import unittest import logging from google.appengine.ext import db from gaetest.stat.models import * class ModelTest(unittest.TestCase): def test_new_entity(self): stat = Stat() self.assertNotEqual(None, stat)
web_tests.py
コントローラのテストはとりあえず空っぽにしておく。
import unittest from webtest import TestApp import django.core.handlers.wsgi import gaetest.stat.views class StatTest(unittest.TestCase): def setUp(self): self.application = django.core.handlers.wsgi.WSGIHandler()
テスト実行
ブラウザでhttp://localhost:8080/test/ を開く(ポート番号は環境に合わせること)
→テスト失敗。
Traceback (most recent call last): File "test\unit_tests.py", line 9, in test_new_entity stat = Stat() NameError: global name 'Stat' is not defined
落ちるのは当たり前で、Statというクラスを宣言していないのであった。
stat/models.py
from google.appengine.ext import db class Stat(db.Model): pass
テスト実行
ブラウザでhttp://localhost:8080/test/ を開く(ポート番号は環境に合わせること)
→今度は成功。
こんな感じで、「何をすれば完了か」をテスト側で定義し、そのテストが失敗することを確認してから、本作業を行い、テストが通ることを確認していく。まだStatクラスは空っぽなので、次はプロパティを定義していく。
- friends_count
- statuses_count
- timestamp
を定義してみる。まずはテストから。
unit_tests.py
import unittest import logging from google.appengine.ext import db from gaetest.stat.models import * class ModelTest(unittest.TestCase): def test_new_entity(self): stat = Stat(followers_count = 100, statuses_count=100000) self.assertEqual(100, stat.followers_count) self.assertEqual(100000, stat.statuses_count) self.assertNotEqual(None, stat.timestamp)
テストすると当然落ちる。まだプロパティ定義してないから。
Traceback (most recent call last): File "test\unit_tests.py", line 12, in test_new_entity_with_properties self.assertEqual(100, stat.followers_count) AttributeError: 'Stat' object has no attribute 'followers_count'
stat/models.py
from google.appengine.ext import db class Stat(db.Model): followers_count = db.IntegerProperty(required = True) statuses_count = db.IntegerProperty(required = True) timestamp = db.DateTimeProperty(auto_now_add = True)
→今度はエラーがでない。
というのをちまちまと繰り返して、テストとコードを交互に書きながらGAEUnitで確認していく、という開発ができるようになる。複数人数でやる場合は、http://localhost:8080/test?format=plain をurllibあたりから呼んで、結果をメールで投げるなりTwitterに書くなりすれば良かろう。
コントローラのテストも同じようにちまちまとやっていく。
web_test.py
import unittest from webtest import TestApp import django.core.handlers.wsgi import main class StatTest(unittest.TestCase): def setUp(self): self.application = django.core.handlers.wsgi.WSGIHandler() def test_index(self): app = TestApp(self.application) response = app.get('/stats') self.assertEqual('301 MOVED PERMANENTLY', response.status) app = TestApp(self.application) response = app.get('/stats/') self.assertEqual('200 OK', response.status) self.assertTrue('test' in response)
→テストは当然落ちる。
settings.py
INSTALLED_APPSにコントローラを追加
INSTALLED_APPS = ( # 'django.contrib.auth', 'django.contrib.contenttypes', # 'django.contrib.sessions', 'django.contrib.sites', 'gaetest.stat.views', )
urls.py
from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^stat/$','gaetest.stat.views.index'), )
views.py
import logging from django.http import HttpResponse from models import Stat def index(request): return HttpResponse("This is a test.")
→今度は通る
こんな感じにコントローラ周りをいじくる時(viewsやurls)も、あらかじめテストを書いておいて「次に何をするのか」を自分に言い聞かせながらぼちぼち書いていくと、考えが発散しなくて楽。