Django オンラインドキュメント和訳 更新。 Revision 5881:5922 (2007/08/20). 「mod_python PythonPath」「./manage.py testserver」「testing」についての記述が更新されています。
以下、 主な変更です。
PythonPath には、アプリケーションから import したいモジュールの各々の 親ディレクトリを入れなければなりません。また、 DJANGO_SETTINGS_MODULE の親ディレクトリも入れねばなりません。対話シェルを使う場合に Python パスを 設定するのと同じです。何かモジュールを import する際には、Python は必ず sys.path の各ディレクトリを順に調べ、その下から該当モジュールを import しようと試み、 import に成功するまで探索を続けます。
分かりやすくするために例を挙げましょう。アプリケーションを /usr/local/django-apps/ の下に配置しているとします (例えば、 /usr/local/django-apps/weblog/ のようにです) 。そして、 mysite プロ ジェクトが /var/www/ 下にあるとしましょう。上の例のように DJANGO_SETTINGS_MODULE を設定している場合には、 PythonPath を以下の ように書かねばなりません:
PythonPath "['/usr/local/django-apps/', '/var/www'] + sys.path"
これで、 import weblog や import mysite.settings が正しく動作します。 コード中で import blogroll していて、 blogroll が weblog/ ディ レクトリの下にあるようなら、 /usr/local/django-apps/weblog/ も PythonPath に加えねばなりません。 import したいモジュールの 親ディレクトリ を Python パスに入れねばならないことに注意してください。
開発版の Django で新たに登場した機能です
指定したフィクスチャを使って、 (runserver と同様に) 開発用サーバを起動 します。
例えば、次のコマンド:
django-admin.py testserver mydata.json
を実行すると、以下のようなステップを実行します:
- Django アプリケーションのテスト 手順に従って、テストデータベース を生成します。
- 指定したフィクスチャを使ってテストデータベースに値を入れます (フィク スチャの説明は loaddata ドキュメントを参照してください)。
- 生成したテストデータベースを使って (runserver と同様に) 開発サー バを実行します。
testserver が便利な局面はいくつかあります:
- 特定のフィクスチャデータに対するビューの動作を調べるための ユニットテスト を書いている際に、ブラウザでの表示を手動で調べるの に testserver を使えます。
- Django アプリケーションを開発していて、「無垢の状態の」データベースを 使って操作してみたいとしましょう。データベースを (前述の dumpdata コマンドを使って) フィクスチャとしてダンプしておき、 testserver を使って Web アプリケーションを実行すれば、アプリケーション上でどんな 操作を行っても、変更はテストデータベースにしか加えられないので、好き にデータベースを「汚せ」ます。
このサーバはローカルホスト上でデフォルトのポートでしか起動せず、 host や port パラメタを受け付けません。
Django でテストを書く方法は主に 2 つあり、それぞれ Python の標準ライブラリ についてくる二つのテストフレームワークに対応しています。フレームワークは以 下の 2 つです:
doctest -- 関数やクラスの docstring (ドキュメンテーション文字列) に埋め込まれたテストで、例えば以下のように Python の対話インタプリタ セッションを模した方法で書かれています
def my_func(a_list, idx): """ >>> a = ['larry', 'curly', 'moe'] >>> my_func(a, 0) 'larry' >>> my_func(a, 1) 'curly' """ return a_list[idx]ユニットテスト (unit test) -- 以下の例のように、テストを unittest.TestCase` のサブクラスのメソッドとして表現したものです
import unittest class MyFuncTestCase(unittest.TestCase) def testBasic(self): a = ['larry', 'curly', 'moe'] self.assertEquals(my_func(a, 0), 'larry') self.assertEquals(my_func(a, 1), 'curly')
好みに応じて、どちらのテストを使ってもかまいませんし、両方のテストを組み合 わせてもかまいません。また、後でほんの少しだけ説明しますが、他のテストフレー ムワークを使っても構いません。
例えば、下の関数には、関数の説明の入った docstring があります:
def add_two(num): "引数に指定した数に 2 を加えて返します。" return num + 2テストはそれ自体素晴らしいドキュメントになることも多いので、テストをそ のまま docstring に入れておけば、ドキュメント化とコードのテストの 両方を 効率的に行えます。
テストランナは、 Django アプリケーションの中の以下のファイルから doctest を 探して実行します:
- models.py ファイル。モジュールレベル、かつ/またはモデルレベルの doctest を記述します。一般には、アプリケーションレベルの doctest はモ ジュールの docstring として記述し、モデルレベルの docstring はモデル クラスの docstring として記述します。
- アプリケーションディレクトリ、すなわち models.py の入ったディレク トリ下に置かれた tests.py という名前のファイル。このファイルは、 モデルに関係しないような doctest を書きたい場合のフックとして使えます。
doctest の文字列は models.py の全てのオブジェクトに対して記述できますが、 慣習的には、アプリケーションレベルの doctest はモジュールの docstring に、 モデルレベルの doctest は各モデルの docstring に配置します。
モデルテストの場合、テストランナが独自にテストデータベースを作成します。す なわち、データベースに対してアクセスするテスト -- 例えば、モデルインスタン スを生成して保存するようなテスト -- が、実運用のためのデータベースに影響を 及ぼすことはありません。 doctest はいずれも「白紙状態」、すなわち、各モデル のデータベーステーブルが空の状態で実行されます (詳しくは、後述のフィクスチャ の節を参照してください) 。
Django の単体テストもまた、doctest と同様、標準ライブラリモジュールの unittest を使います。このモジュールは、 doctest とは違った、 クラスベース のやり方でテストを定義します。
doctest と同様、 Django のテストランナは、以下の二つの場所からユニットテス トを探します:
- models.py ファイル。テストランナはこのモジュールから unittest.TestCase のサブクラスを探します。
- アプリケーションディレクトリ、すなわち models.py の入ったディレク トリ下に置かれた tests.py という名前のファイル。上と同様に、テス トランナはこのモジュールから unittest.TestCase のサブクラスを探し ます。
開発バージョンの Django には、あるモジュールのテストスイートを定義する方法 をもう一つ提供しています models.py や tests.py で suite() メ ソッドを定義している場合、 Django のテストランナはこのメソッドを使ってテス トスイートを構築します。この仕様は、ユニットテストにおいて 推奨されているテストスイートの構築方法 に従っています。複雑なテストスイー トの構築方法についての詳細は Python のドキュメントを参照してください。
テストを実行すると、まずテストランナ自身の初期化メッセージが表示されます:
Creating test database... Creating table myapp_animal Creating table myapp_mineral Loading 'initial_data' fixtures... No fixtures found.
このメッセージは、テストランナがテストデータベースを作成したことを示してい ます。テストデータベースは、空の、何もない状態から作成したデータベースで、 (モデルテストのような) データベースの必要なテストで使われます。
「本番用の」(実運用の) データベースのことは心配いりません。 Django はテスト 用にまったく別のデータベースを作成します。このデータベースの名前は、 DATABASE_NAME に指定したデータベース名の前に test_ を付けたものにな ります。テストデータベースの名前をデフォルト意外の値にしたければ、 TEST_DATABASE_NAME 設定を使って名前を指定します。
テスト用に別のデータベースを使うことを除けば、テストランナは設定ファイルの データベースに関する他の設定、 DATABASE_ENGINE, DATABASE_USER, DATABASE_HOST などをそのまま使います。テストデータベースは DATABASE_USER の権限で作成されるので、このユーザは新たに生成されたデー タベースを操作する権限を備えていなければなりません。
エラー出力の詳細はこのドキュメントの範囲を超えるので解説はしませんが、ほと んど直感的に理解できる内容のはずです。詳しくは、 Python の unittest ラ イブラリのドキュメントを参照してください。
スクリプトのリターンコードは失敗したテストや出力のおかしかったテストの総数で す。全てのテストにパスしていれば、リターンコードは 0 です。この仕様は、テス トランナをシェルスクリプト上で動かしたり、テストが成功したかどうかをテスト ランナのレベルで調べたい場合に便利です。
テストにパスしたか否かに関係なく、テストを全て実行し終えると、テストデータ ベースは消去されます。
Django は、テストを書くときに便利なツールをいくつか提供しています。
テストクライアント (test client) は、簡単なダミーブラウザとして動作する Python のクラスです。テストクライアントを使うと、ビューをテストしたり、 プログラムを使ってDjango で作られたアプリケーションとやりとりできます。
テストクライアントを使ってできることをいくつか挙げましょう:
- ある URL に対する GET や POST をシミュレートでき、低水準の HTTP (レスポ ンスヘッダや状態コード) 情報から、ページの内容まで、リクエストに対するレ スポンスの全てを調べられます。
- 特定の URL に対して正しいビューが呼び出されるかどうかを調べられます。
- 特定のリクエストに対して、特定のテンプレートを使ったレンダリングが行わ れ、その際に特定の値が入ったコンテキストが使われているかどうかを調べら れます。
テストクライアントは Twill や Selenium やその他のブラウザ自動化フレームワー クを置き換えようとするものではありません。 Django のテストクライアントはもっ と別の部分に焦点を当てているのです。すなわち:
- 正しいビューが呼び出され、ビューが正しいコンテキストデータを生成してい るかどうかは、 Django のテストクライアントを使って調べてください。
- Twill や Selenium は、 レンダリング済みの HTML や、 JavaScript の機能 のような Web ページの ビヘイビア のテストに使ってください。
網羅的なテストスイートでは、両方のタイプのテストを組み合わせて使うはずです。
テストクライアントを使うには、 django.test.client.Client をインスタンス 化して、 Web ページを取得します
>>> from django.test.client import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...'
上の例でわかるように、 Client は Python 対話インタプリタのセッション中 でもインスタンス化できます。
テストクライアントの動作には重要な点がいくつかあります:
テストクライアントを実行するために Web サーバを起動する必要は ありません 。実際、 Web サーバがまったく動いていなくても、テストは 何の問題もなく実行できるのです。というのも、テストクライアントは HTTP 通信のオーバヘッドを回避して、 Django フレームワークに直接アクセスし ているからです。このからくりによって、ユニットテストを高速に実行でき ます。
ページを取得するときには、ドメインを含まない パス部分だけ を指定す るよう気をつけてください。例えば、以下の呼び出し:
>>> c.get('/login/')は正しいですが、次の呼び出し:
>>> c.get('http://www.example.com/login/')は正しくありません。
Django のプロジェクトから生成されていない Web ページは、テストクライ アントで取得できません。Django 以外の Web ページを取得したければ、 urllib や urllib2 のような Python 標準ライブラリを使ってください。
テストクライアントは URL の解決に ROOT_URLCONF に指定された URLconf を使います。
上の例は Python の対話インタプリタ中でも動作しますが、一部のテンプレー ト関連の機能などは テストの実行中だけ でしか使えません。
というのも、 Django のテストランナは、あるビューでどのテンプレートが ロードされるかを決定するためにちょっとした黒魔術的なコードを使ってい るからです。この黒魔術 (実際には、メモリ上のテンプレートシステムに対 するパッチ) は、テスト実行時にしか適用されません。
リクエストの生成には、 django.test.client.Client クラスを使います。 Client は引数なしで生成します。:
>>> c = Client()
Client のインスタンスからは、以下のメソッドを呼び出せます:
path に対する GET リクエストを行い、 Response オブジェクトを返 します。 Response オブジェクトについては後で説明します。
引数 data は辞書オブジェクトで、キー/値のペアが GET データのペイロー ドの生成に使われます。例えば:
>>> c = Client()
>>> c.get('/customers/details/', {'name':'fred', 'age':7})
は、以下のような GET リクエストの送信と同じです:
/customers/details/?name=fred&age=7
path に対する POST リクエストを行い、 Response オブジェクトを返 します。
引数 data は辞書オブジェクトで、キー/値のペアが POST データのペイ ロード生成に使われます。例えば:
>>> c = Client()
>>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
は、以下のパス:
/login/
への POST リクエストで、以下の POST データ:
name=fred&passwd=secret
を伴います。
content_type を指定した場合 (例えば XML ペイロードの場合には text/xml)、引数 data の中身は POST リクエストそのままで送信され、 Content-Type ヘッダに content_type の値を使います。
content_type を指定しなければ、 data の中身は multipart/form-data で送信されます。この場合、 data の中のキー/ 値のペアは、マルチパートメッセージにエンコードされ、 POST データのペイ ロード生成に使われます。
あるキーに対して複数の値を提出 (submit) する場合 (例えば、 <select multiple> の選択結果を指定する場合など) は、キーに対する値 をリストやタプルにしてください。例えば、``choices`` という名前のフィー ルドから 3 つの行を選択して提出したければ:
``{'choices': ('a', 'b', 'd')}``
のようにします。
ファイルの送信には特別な扱いが必要です。ファイルの POST を行う場合、以 下のように、ファイル名フィールドの名前をキーに、アップロードしたいファ イルのファイルハンドルを値に設定します:
>>> c = Client()
>>> f = open('wishlist.doc')
>>> c.post('/customers/wishes/', {'name':'fred', 'attachment':f})
>>> f.close()
(attachment という名前には特に意味はありません。ファイルを処理する コードで使いたい名前を指定してください)
送信するファイルのハンドルは、 post() 後に手動で閉じねばならないの で注意してください。
開発バージョンの Django で新たに追加されました
Django の 認証システム を使っていて、ログイン済みのユーザを扱う必要 がある場合、テストクライアントの login() メソッドを使えば、サイトへ ログインするユーザをシミュレートできます。
このメソッドを呼び出すと、テストクライアントはログインに必要なクッキー とセッションデータを持つようになり、テスト対象のビューのうち、ログイン の必要なテストをパスできるようになります。
credentials 引数の形式は、使っている 認証バックエンド によって変 わります (認証バックエンドは AUTHENTICATION_BACKENDS で設定します)。 Django が提供している標準の認証バックエンド (ModelBackend) を使う場 合、 credentials にはユーザのユーザ名とパスワードをキーワード引数で 指定します:
>>> c = Client() >>> c.login(username='fred', password='secret') >>> # これでログイン保護されたビューに入れるようになりました。
認証バックエンドを変えた場合、このメソッドは違った認証情報を要求するか もしれません。その場合、 login() は、認証バックエンドの authenticate() メソッドが必要とする認証情報をそのまま要求します。
login() は、指定した認証情報が承認され、ログインに成功した場合に True を返します。
このメソッドを使には、事前にユーザを登録しておかねばなりません。上で説 明したように、テストランナはテストデータベースを使ってテストを実行する ので、デフォルトではユーザが登録されていません。そのため、運用環境で使 えるユーザはテスト環境では使えないのです。テストスイートの一環として、 手動で (Django のモデル API を使って) ユーザを作成するか、フィクスチャ でユーザを登録してください。
Django の 認証システム を使っている場合、 logout() メソッドで、 サイトからユーザをログアウトさせる効果をシミュレートできます。
このメソッドを呼び出すと、テストクライアントはクッキーとセッションデー タを全て消去して、デフォルトの状態に戻します。それ以降のリクエストは、 ビューからは AnonymousUser から送信されたリクエストとみなされます。
get() および post() メソッドは、いずれも Response オブジェクト を返します。 Response オブジェクトは Django のビューが返す HttpResponse オブジェクトと同じ ではありません 。このオブジェクトは HttpResponse よりも単純で、テスト用の便利なデータをいくつか備えています。
Response オブジェクトには以下のようなプロパティがあります:
属性 説明 status_code レスポンスの HTTP 状態コードです。全ての HTTP 状態コー ドのリストは RFC2616 を参照してください。 content レスポンスの本体部分 (body) です。ビューがレンダリン グによって生成した最終的なページコンテンツか、エラー メッセージ (302 リダイレクトの場合のリダイレクト先 URL など) です。 template 最終的なページコンテンツのレンダリングに使われた Template のインスタンスです。テンプレートをファイルか らロードした場合、テンプレートのファイル名を template.name で調べられます ('admin/index.html' のような形式の文字列です)。
複数のテンプレートをレンダリングしている場合 (例えば テンプレートの継承 を使っている場合) 、 template はレンダリング順に並んだ Template オブ ジェクトのリストになります。
context レスポンスのページコンテンツのレンダに使われた Context オブジェクトです。
template と同様、複数のテンプレートを使ってレンダ リングを行った場合、 context は Context オブジェ クトをレンダリングで使った順に並べたリストになります。
テストクライアントのアクセス先のビューが例外を送出するような場合、テストケー ス内で例外にアクセスできます。例外のテストを行うには、通常の try...catch ブロックか、 unittest.TestCase.assertRaises() を使いま す。
ただし、 Http404 や PermissionDenied, SystemExit といった例外は テストケースからアクセスできません。 Django はこれらの例外を内部的に捕捉し て、対応する適切な HTTP 応答コードに変換してしまうからです。これらの例外に 対しては、 response.status_code のチェックで対応してください。
テストクライアントの動作はステートフルです。あるレスポンスにクッキーが入っ ていると、クッキーはテストクライアント中に保存され、それ以降の get() や post() リクエストで使われます。
テストクライアントは、クッキーの有効期限ポリシを守りません。クッキーを期限 切れにしたければ、該当クッキーを client.cookies から手動で削除するか、 新たな Client インスタンスを生成してください (全てのクッキーを除去しま す)。
テストクライアントには、永続セッション情報 (persistent state information) を保存するためのプロパティが二つあります。これらのプロパティは、必要に応じ てテスト条件の一部として検査できます。
プロパティ 説明 cookies Python の SimpleCookie 型のオブジェクトで、 全てのクライアントクッキーの現在値が入っています。 詳しくは Cookie モジュールのドキュメント を参照 してください。 session セッション情報の入った辞書ライクなオブジェクトです。 詳しくは セッションのドキュメント を参照してくださ い。
テストクライアントを使った簡単なユニットテストを以下に示します
import unittest
from django.test.client import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
# unittest を使う場合、毎回 Client を生成する必要があります。
self.client = Client()
def test_details(self):
# GET リクエストを発行します。
response = self.client.get('/customer/details/')
# レスポンスが 200 OK であるか調べます。
self.failUnlessEqual(response.status_code, 200)
# レンダリングされるコンテキストの customers の長さが 5 である
# か確かます。
self.failUnlessEqual(len(response.context['customers']), 5)
開発バージョンの Django で新たに追加されました
django.test.TestCase クラスのインスタンス内に入っているテストケースは全 て、デフォルトの テストクライアント にアクセスできます。このテストクライ アントは self.client で参照できます。テストクライアントはテストごとに再 生成されるので、テスト間でクッキーのような状態情報が継承される心配はありま せん。
例えば、以下のコード
import unittest
from django.test.client import Client
class SimpleTest(unittest.TestCase):
def test_details(self):
client = Client()
response = client.get('/customer/details/')
self.failUnlessEqual(response.status_code, 200)
def test_index(self):
client = Client()
response = client.get('/customer/index/')
self.failUnlessEqual(response.status_code, 200)
では、各テストで Client をインスタンス化していますが、実際は次のコード のように、 self.client を参照するだけでよいのです
from django.test import TestCase
from django.test.client import Client
class SimpleTest(TestCase):
def test_details(self):
response = self.client.get('/customer/details/')
self.failUnlessEqual(response.status_code, 200)
def test_index(self):
response = self.client.get('/customer/index/')
self.failUnlessEqual(response.status_code, 200)
