Fixture + GoogleAppEngine
fixtureをGoogleAppEngineのDataStoreテストで使う。json編
参考 Using Fixture To Test A Google App Engine Site
DataSetsをjson化するのにfixture.dataset.converterでdataset_to_jsonが提供されていたりもする。ただdate_created(auto_now_add=True)のようなフィールドはDataSetsで前もって値を指定できないので、ここでjson化したデータを、実際(date_createdの付いた)のjsonのレスポンスと比較しても同じにはならない。
.zshrc
gaedir=/usr/local/google_appengine if [ -d $gaedir ] ; then ; export PATH=${PATH}:$gaedir export PYTHONPATH=${PYTHONPATH}:${gaedir}:${gaedir}/lib/antlr3:\ ${gaedir}/lib/cacerts:${gaedir}/lib/django:${gaedir}/lib/ipaddr:\ ${gaedir}/lib/webob:${gaedir}/lib/yaml/lib fi;
$ source ~/.zshrc $ pip install WebTest $ pip install nose $ pip install NoseGAE
app.yaml
application: sampleblog # _ はapplicationの識別子として使えないので注意 version: 1 runtime: python api_version: 1 handlers: - url: /.* script: blog.py
blog.py
#coding:utf-8 import logging from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.ext import db from django.utils import simplejson class Entry(db.Model): title = db.StringProperty() body = db.TextProperty() date_created = db.DateTimeProperty(auto_now_add=True) class Comment(db.Model): entry = db.ReferenceProperty(Entry) comment = db.TextProperty() date_created = db.DateTimeProperty(auto_now_add=True) class EntriesHandler(webapp.RequestHandler): def get(self): self.response.headers['Content-Type'] = 'application/json;charset=utf-8' entries = [] for entry in Entry.all(): comments = [] for comment in Comment.all().filter("entry =", entry): comment_dict = \ dict(comment=comment.comment, date_created=comment.date_created.isoformat()) comments.append(comment_dict) entry_dict = dict(title=entry.title, body=entry.body, date_created=entry.date_created.isoformat(), comments=comments) entries.append(entry_dict) json = simplejson.dumps(entries, indent=True) #logging.info(json) self.response.out.write(json) routing = [('/entries', EntriesHandler)] application = webapp.WSGIApplication( routing, debug=True) def main(): run_wsgi_app(application) if __name__ == '__main__': main()
tests/datasets.py (http://fixture.googlecode.com/hg/fixture/examples/google_appengine_example/tests/datasets.py)
from fixture import DataSet class EntryData(DataSet): class great_monday: title = "Monday Was Great" body = """\ Monday was the best day ever. I got up (a little late, but that's OK) then I ground some coffee. Mmmm ... coffee! I love coffee. Do you know about <a href="http://www.metropoliscoffee.com/">Metropolis</a> coffee? It's amazing. Delicious. I drank a beautiful cup of french pressed <a href="http://www.metropoliscoffee.com/shop/coffee/blends.php">Spice Island</a>, had a shave and went to work. What a day! """ class CommentData(DataSet): class monday_liked_it: entry = EntryData.great_monday comment = """\ I'm so glad you have a blog because I want to know what you are doing everyday. Heh, that sounds creepy. What I mean is it's so COOL that you had a great Monday. I like Mondays too. """ class monday_sucked: entry = EntryData.great_monday comment = """\ Are you serious? Mannnnnn, Monday really sucked. """
load_data_locally.py
(http://fixture.googlecode.com/hg/fixture/examples/google_appengine_example/load_data_locally.py)一部修正
import sys import os import optparse from fixture import GoogleDatastoreFixture from fixture.style import NamedDataStyle def main(): p = optparse.OptionParser(usage="%prog [options]") default = "/tmp/dev_appserver.datastore" p.add_option("--datastore_path", default=default, help=( "Path to datastore file. This must match the value used for " "the same option when running dev_appserver.py if you want to view the data. " "Default: %s" % default)) default = "/tmp/dev_appserver.datastore.history" p.add_option("--history_path", default=default, help=( "Path to datastore history file. This doesn't need to match the one you use for " "dev_appserver.py. Default: %s" % default)) default = "/usr/local/google_appengine" p.add_option("--google_path", default=default, help=( "Path to google module directory. Default: %s" % default)) (options, args) = p.parse_args() if not os.path.exists(options.google_path): p.error("Could not find google module path at %s. You'll need to specify the path" % options.google_path) groot = options.google_path sys.path.append(groot) sys.path.append(os.path.join(groot, "lib/django")) sys.path.append(os.path.join(groot, "lib/webob")) sys.path.append(os.path.join(groot, "lib/yaml/lib")) from google.appengine.tools import dev_appserver import blog from tests import datasets config, explicit_matcher = dev_appserver.\ LoadAppConfig(os.path.dirname(__file__), {}) dev_appserver.SetupStubs( config.application, clear_datastore=False, # just removes the files when True datastore_path=options.datastore_path, history_path=options.history_path, blobstore_path=None, # 追加 KeyError: 'blobstore_path' を避ける login_url=None) datafixture = GoogleDatastoreFixture(env={'EntryData': blog.Entry, 'CommentData': blog.Comment}) data = datafixture.data(datasets.CommentData, datasets.EntryData) data.setup() print "Data loaded into datastore %s" % \ (options.datastore_path or "[default]") if __name__ == '__main__': main()
tests/test_entries.py
#coding:utf-8 import unittest from fixture import GoogleDatastoreFixture from webtest import TestApp import blog from datasets import CommentData, EntryData from django.utils import simplejson datafixture = GoogleDatastoreFixture(env={'EntryData': blog.Entry, 'CommentData': blog.Comment}) class TestListEntries(unittest.TestCase): def setUp(self): self.app = TestApp(blog.application) self.data = datafixture.data(CommentData, EntryData) self.data.setup() def tearDown(self): self.data.teardown() def test_entries(self): response = self.app.get("/entries") assert simplejson.dumps(EntryData.great_monday.title) in response assert simplejson.dumps(EntryData.great_monday.body) in response assert simplejson.dumps(CommentData.monday_liked_it.comment) \ in response assert simplejson.dumps(CommentData.monday_sucked.comment) \ in response
Create custom datasets.
$ ./load_data_locally.py --datastore_path=./my.datastore
Run server with custom data.
$ dev_appserver.py . --datastore_path=./my.datastore
Run tests.
$ nosetests -v --with-gae test_entries (tests.test_entries.TestListEntries) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.296s OKPosted: May 26th, 2010 | Author: yamakk | Filed under: 技術 | Tags: database, fixture, gae, googleappengine, json, python | No Comments »