Pyramid Testing

さて、もう間近にせまった PyCon Apac 2013 ですが、 わたくし パッケージングの今と未来 にて登壇します。

ところで、これ以外にもCFPを提出していまして、没ネタが2つほどあります。

  • sqlalchemyの錬金術
  • testing pyramid

(´・ω・`)実際のとこ、pyramidの話したかったんですけどねぇ....

Pyramidでのテスト

まずはpylonsprojectのドキュメント Unit Testing Guidelines があります。 これはテストのテクニックではなく作法ですが、テスト内容を明確にするというのが重要な点かなと思います。

Pyramidなところはそんなとこかもしれません。 どこぞのガラパゴスな進化をしたフレームワークと違い、モダンなテストツールをそのまま使えます。(Pythonのフレームワークなのだから当たり前ですけども)

テストケース

pytestやnoseなどはディスカバリー機能を持ってるので、 test_* といった名前の関数でよいですが、伝統的な unittest フレームワークでは、 TestCase クラスを継承します。 フィクスチャとしてよく用いられるダミークラスは pyramid.testing に用意されています。 たとえば、ビュー関数のテストをする場合は pyramid.testing.DummyRequestrequest オブジェクトを作ってビュー関数に渡します。

def index(request):
    return dict(message=request.params['message'])
from pyramid import testing

def test_index():
    request = testing.DummyRequest(params=dict(message="Hello"))
    result = index(request)
    eq_(result, dict(message='Hello'))

ああ、まったく普通のテストですね。

データベース関連のテスト

とりあえず ZODB 使う場合はなにも迷うことはありません。 オブジェクトなので、なにも気にせず単なるクラスとしてテストしましょう。 トランザクションや永続化のようなものは、Persistentクラスやtransactionが面倒を見てくれます。

SQLAlchemy を使う場合もたいていは永続化層のことは考えずにすみます。 zope.sqlalchemyとtransactionによって制御されるので、アプリケーションコードで明示的なcommitなどはしないようにしましょう。 sqlalchemyのsessionから取り出されたオブジェクトは状態が更新された場合に自動でUPDATEを行います。 新規に作成された場合は自動でINSERTされず、sessionに参加させねばなりません。 方法は2通りで、 session.add(obj) で明示的に追加する方法と、既にsessionに参加しているオブジェクトと関連付けします。

pyramidはroute(URLパターンとかそういうやつ)に factory を設定でき、factoryの結果を requestが contextとして持っています。 最初のモデルのロードを factory で行えば、新規オブジェクトは context に追加するだけでよいので、ビュー内でsessionを使う必要はなくなります。

def load_site(request):
    try:
        return Site.query.filter(Site.name=='default').first()
    except NoResultFound:
        raise HTTPNotFound

config.add_route('top', '/', factory=load_site)


@view_config(route_name='top', method='POST')
def new_document(request):
    # 入力チェックなど
    document = Document(**request.params)

    request.context.documents.append(document)

    return HTTPFound(request.route_url('document', name=document.name)

load_site のテストはデータベースが必要です。 しかし、 new_document のユニットテストでは、データベースは必要ありません。

機能テスト

pyramidはwsgiフレームワークなので、 WebTest を使ってテストできます。 wsgiミドルウェアを使う場合は、PasteDeployなどを使ってミドルウェア適用済のwsgiアプリをロードしてから、 TestApp を作成しますが、 pyramidは最近あまりwsgiミドルぅエアを使わない傾向にあります。 その場合は単純に、pyramid.config.Configurator.make_wsgi_app を呼ぶだけです。多くの場合、アプリケーションのmain関数を呼ぶだけでしょう。

 def test_app():
     app = your.app.main({}, **test_settings)
     app = webtest.TestApp(app)

     res = app.get('/')
     res.form['name'] = 'test-document'
     res.form['data'] = """\

This is First Document
----------------------------------

test
"""
     res = res.form.submit()
     eq_(res.location, 'http://example.com/test-document')
     res = app.get(res.location)

     # ...

まとめ

(´・ω・`)というわけで、pyramid特有となると、context のところくらいしかありませんでした。 餅は餅屋なので、テストツールが提示している書き方をすれば十分ですね。

Comments !

blogroll

social