TestCaseを拡張しよう

Djangoでのテストを書くときに、URLにアクセスしてステータスコード見るだけってテストをコピペで量産するのはかっこ悪いので、どうにかする例。
test.pyにTestCaseをちょっと拡張したクラスを書いておく

test.py

from django.test import TestCase

class BaseURLTestCase(type):
    def __new__(cls, name, bases, dict):
        counter = 0
        for url in dict['url_list']:
            def _outer(url):
                def _url_test(self):
                    response = self.client.get(url[0])
                    self.assertEquals(response.status_code, url[1],
                            '%d != %d %s' % (response.status_code, url[1], url[0]))
                return _url_test
            dict['test_url_%d' % counter] = _outer(url)
            counter += 1
        return type.__new__(cls, name, bases, dict)

class URLTestCase(TestCase):
    url_list = ()
    __metaclass__ = BaseURLTestCase

test〜で始まるメソッドがあればよいので、メタクラスで生成するようにした。
これを使うと、指定したURLにGETアクセスしてステータスコードを見るテストが、こんな感じに書ける。

tests.py

from test import URLTestCase

class GuestBookURLTestCase(URLTestCase):
    url_list = (
        (r'/', 200),
        (r'/admin/', 200),
        (r'/foo/', 404),
    )

この場合test〜で始まるメソッドが3つできるので、テストを実行すると

Q:\Python\_sandbox\myproject>python manage.py test guestbook
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table guestbook_greeting
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model
...
----------------------------------------------------------------------
Ran 3 tests in 0.032s

OK
Destroying test database...

という感じになる。
これぐらい簡単ならまあ書く気も起こるかなあ。

追記

コードに誤りがありました。_url_testは_outerで一つラップしてやらないとクロージャにならない。直しておきました。