LLRingのDjango製アプリ(フライング) | Top | RailsのPagination

 

DjangoのViewをテストする

最近追加されたテスティングフレームワークを試したみた。

Djangoは基本的にはPython由来のdoctestやunittestを用いてテストを行う。最近追加されたテスティングフレームワークは、アプリケーションの直下にあるmodels.pyやtests.pyを自動的にAllテストしてくれるというもの+Viewのテストを行う疑似ブラウザともいえるClientというクラス。

Railsと同じく、Djangoもテストの前にテスト用データベース・テーブルを生成し、初期データを流し込み(fixtureは現在実装中)、テストを行い、テスト用データベースを破棄するという流れ。おいおい、そんな流れは業務系とか既存データベース使うアプリにはできんぞ、せめてビューとかシノニムとかに気づいてくれよー。
とか思いつつ。(現実的には、デフォルトのTEST_RUNNER設定であるdjango.test.simple.run_testsをそのままは利用せずに、ちょっと振る舞いをかえたTEST_RUNNERを利用することになるんだろうな。Djangoは素直にコードが書いてあるから自作も簡単だろう。)

doctest

きっとモデルやマネージャに対して有効な気がするテスト記述方式。つか、面白い。
manage.py test --settings=djengel.custom_settings とすると、テストが実施される。
このテストは、費目クラスの削除や表示フラグオフに対するテスト。削除も表示フラグオフも同じ動作になる。削除してもデータが消されないという肝心のテストが無いな・・。
class ExpenseItemManager(models.Manager) :
    """費目モデルのマネージャ。デフォルトのQuerysetに、非表示は取得しない、という条件を付与している。
    """
    def get_query_set(self) :
        return super(ExpenseItemManager, self).get_query_set().filter(visible=True)


class ExpenseItem(models.Model) :
    """費目を表すモデル。

    デフォルト動作の変更をいくつか実装してある。

        - デフォルトのdeleteを上書き
        - デフォルトのマネージャの上書き
        - カスタムマネージャの追加。
    >>> invisItem = ExpenseItem.objects.create(name="Test Invisible", max_amount=4000, food_cost=False, visible=True)
    >>> delItem   = ExpenseItem.objects.create(name="Test Delete"   , max_amount=4000, food_cost=False, visible=True)
    >>> before = ExpenseItem.objects.all()
    >>> invisItem in before
    True
    >>> delItem in before
    True
    >>> invisItem.visible=False
    >>> invisItem.save()
    >>> delItem.delete()
    >>> after = ExpenseItem.objects.all()
    >>> invisItem in after
    False
    >>> delItem in after
    False
    
    """
    name = models.CharField(_('Expense Item Name'), blank=False, maxlength=30)
    max_amount = models.IntegerField(_('Max Amount'), blank=False, default=0, help_text=_('Max amount per month.'))
    food_cost = models.BooleanField(_('Food Cost Flag'), default=False, help_text=_('weather food cost or not'))
    visible = models.BooleanField(_('Visible Flag'), default=True, help_text=_('if expense item is invisible, you can\'t edit anymore.'))
    objects = ExpenseItemManager() #デフォルトのマネージャをカスタムマネージャに変更
    summary_objects = ExpenseItemSummaryManager() #サマリー用のマネージャを登録

    class Admin :
        list_display = ("name", "max_amount", "visible", "food_cost")
        ordering = ['id']

    class Meta :
        verbose_name = _('Expense Item')
        verbose_name_plural = _('Expense Items')

    def __str__(self) :
        """オブジェクトの人間可読表現
        """
        return self.name

    def delete(self) :
        """費目を物理削除すると関連する出費も含めて削除されてしまうため、モデルを利用しての削除はできないようにしてある。

        代わりに、削除を呼ぶと「表示フラグ」をオフにするようにし、同時にマネージャをカスタムマネージャとし、デフォルトのQuerysetに「表示フラグ」オンという条件を入れるようにした。これにより、費目を管理画面から削除すると、「削除した費目を管理画面で編集できない」「削除した費目は出費の追加画面に候補として出てこない」という動作を実現している。
        """
        self.visible = False
        self.save()


unittest

同様のテストをunittestを使って記述するとこうなる。まぁ、普通。testメソッドで例外が発生してもきちんとtearDownが走る。
import unittest

from djengel.squander.models import ExpenseItem

class ExpenseItemTest(unittest.TestCase) :

    def setUp(self) :
        self.invisItem = ExpenseItem.objects.create(name="Test Invisible", max_amount=4000, food_cost=False, visible=True)
        self.delItem   = ExpenseItem.objects.create(name="Test Delete"   , max_amount=4000, food_cost=False, visible=True)

    def tearDown(self) :
        pass

    def test_normal(self) :
        before = ExpenseItem.objects.all()
        assert self.invisItem in before
        assert self.delItem in before	

    def test_delete(self) :
        self.delItem.delete()
        after = ExpenseItem.objects.all()
        assert self.delItem not in after

    def test_invisible(self) :
        self.invisItem.visible=False
        self.invisItem.save()
        after = ExpenseItem.objects.all()
        assert self.invisItem not in after


Viewのテスト

普段業務で使っているJavaでは、特定のリクエストをした際に、HttpServletRequestに何が保存されているかを確認している。FlexからたたくJavaの場合は単にリターンをチェックしている。
Viewテストで行いたいのは、この程度。可能か?
結論から言えば、余裕でまかなえる。以下のテストのtest_loginを見てもらうと、responseからcontextを取り出すためにおかしなコードを挟んでいることに気づくと思う。
このテストでリクエストを出しているpathは、権限チェックが含まれるviewなので、ログインしていない状態でリクエストを出すと1.ログイン画面にリダイレクトされる、2.ログイン画面からClient.loginでログイン用のViewにPostされログインを行う、3.test_loginでアクセスしようとしていたViewの結果がかえる、のでresponseが3回疑似ブラウザに渡されている。response.contextはこの3回の結果がリストとなって格納されている。contextにアクセスできるので、Viewで行った処理が正常かどうかを追うことができる(しかもリダイレクトされることまで追うことができる)。
ちなみに、Viewのテストにテストサーバの起動は不要。WSGIを使って疑似的(実は疑似ではない?)にリクエストを処理している。かなりたくさんの情報が取り出せる(responseの内容を表示してみれば何が入っているのかすぐわかる)ので、十分テスト可能。
import unittest

from djengel.squander.models import ExpenseItem

from django.test.client import Client
from django.contrib.auth.models import User, Permission

class SquanderViewTest(unittest.TestCase) :
    def setUp(self) :
        self.client = Client()
        self.user = User.objects.create_user('mam', '', 'mampass')
        view_permission = Permission.objects.get(codename__exact='can_view')
        self.user.user_permissions = [view_permission]

    def tearDown(self) :
        self.user.delete()

    def test_login(self) :
        respon = self.client.login('/djengel/summary/2006/08/','mam', 'mampass')
        last_response = respon.context[len(respon.context) - 1]
        assert '2006/07/' == last_response.get('prev_month', False)
        print last_response.__dict__

    def test_login_failed(self) :
        assert not self.client.login('/djengel/summary/2006/08/','dad', 'mampass')

Taged with:,,,,

urihttp://www.everes.net/2006/sep/03/django-view-testing/

Entry Date:2006-09-03 23:46

Author:makoto

ping url:http://www.everes.net/2006/sep/03/django-view-testing/tbping/

subscribe:feed with LDR

Comments

1 by ymasuda

07:31 on

2006-09-04

#3711 対応済みです :)

2 by nakagami

09:27 on

2006-09-04

テスト入りのソースアーカイブはどっかに公開されないもんでしょうか?

3 by everes

11:08 on

2006-09-04

nakagamiさんにご指摘いただいた初期SQLの問題とf3cさんのMySQLパッチをいい感じに適用して、ほんの少しのテストコードを含ませたものを、近いうちに作成します。
が、ちょっと本業が忙しく(つらいスキーマのOracleにDjangoを突っ込んでいます)、週末待ちかもしれません。

> ymasudaさん
ひじょーに!助かります。

Add Comment

コメント追加








What's Next
Tags
Blog Archive
Project
  • » pymagnolia

    ma.gnolia.comのAPIを操作するPythonライブラリ。

  • » django-ja

    Djangoと日本の仲間たち。
    Djangoに関するニュースや、「仲間たち」のブログエントリをアグリゲートしています。

  • » rhaco

    PHPのWebフレームワーク。 PHPらしさを失わず直感的に記述できることを目指している。 Djangoと同じく、他のライブラリに依存しない。また、Djangoっぽいテンプレートの作り方も可能。

  • » Tracka

    スモールチーム、家族用の知共有ウェブアプリケーション。
    テキスト、画像、youtube動画、stage6動画等をトラック!

  • » BMO

    BMOは、本、CD、DVD、ゲームに印刷されているバーコードをiSightで読み取り、AmazonのAPIを利用して画像や情報をmacに保存します。iSightのついているmacで利用してください。Leopardと呼ばれているOSX 10.5以上が必要です。 保存した情報は、OSX Leopard(10.5)から搭載されたCover FlowとQuick Lookという仕組みを利用して表示します。