テストを書いて実行するLink to this heading
ドキュメントは2つの大きなセクションに分けられます。前半のパートでは、Django でのテストの書き方を説明します。後半では、テストの実行の仕方について説明します。
テストを書くLink to this heading
Django のユニットテストには、Python スタンダードライブラリのモジュール、unittest を使用します。このモジュールは、テストをクラスベースのアプローチで定義します。
次の例では、 unittest.TestCase のサブクラスである django.test.TestCase から、テスト用の新しいサブクラスを作っています。各テストをトランザクションの内側で実行することで、独立性を実現しています。
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
cat = Animal.objects.get(name="cat")
self.assertEqual(cat.speak(), 'The cat says "meow"')
cat.sound = "hiss"
cat.save(update_fields=["sound"])
self.assertEqual(cat.speak(), 'The cat says "hiss"')
テストを実行する と、テストユーティリティのデフォルトの動作として、ファイル名が test で始まるファイル内のすべてのテストケースクラス (つまり unittest.TestCase のサブクラス) を見つけ、それらのテストケースクラスから自動的にテストスイートを構築し、そのテストスイートを実行します。
unittest の詳細については、Python のドキュメントを読んでください。
テストの実行Link to this heading
テストが書けたら、プロジェクトの manage.py ユーティリティの test コマンドでテストが実行できます。
$ ./manage.py test
Test discovery is based on the unittest module's built-in test
discovery. By default, this will discover tests in
any file named test*.py under the current working directory.
/manage.py test に好きな数の「テストラベル」を与えることで、特定のテストを指定することもできます。各テストラベルには、パッケージ、モジュール、 TestCase サブクラス、テストメソッドへのドット区切りの Python パスを指定します。たとえば、以下のように指定します。
# Run all the tests in the animals.tests module
$ ./manage.py test animals.tests
# Run all the tests found within the 'animals' package
$ ./manage.py test animals
# Run just one test case class
$ ./manage.py test animals.tests.AnimalTestCase
# Run just one test method
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak
ディレクトリ下に置かれたテストを探索するために、ディレクトリのパスを指定することもできます。
$ ./manage.py test animals/
-p (または --pattern) オプションを使って、カスタムのファイル名のパターンマッチを指定すれば、テストファイルの名前が test*.py というパターンとは違っていても実行できます。
$ ./manage.py test --pattern="tests_*.py"
テストの実行中に Ctrl-C を押すと、テストランナーは現在実行中のテストが完了するのを待って、gracefully にテストを終了します。graceful な終了では、テストランナーは失敗したテストの詳細を出力し、実行したテストの数と、エラーおよび失敗したテストの数をレポートし、通常通りにテストデータベースを破棄します。そのため、 Ctrl-C を押すのは、たとえば、 --failfast オプションを付けるのを忘れて、一部のテストが予期せず失敗して、すべてのテストが終わるのを待たずにすぐにその失敗の詳細を知りたいような場合に大変役に立ちます。
現在実行中のテストの終了も待ちたくないときは、もう一度 Ctrl-C を押すことで、テストを graceful ではなく、すぐに強制終了できます。その場合、強制終了前に実行していたテストの詳細はリポートされず、実行中に作られたテストデータベースも破棄されません。
test データベースLink to this heading
データベースを必要とするテスト (すなわち、モデルテスト) には、"実際の" (production) 環境のデータベースは使用しません。代わりに、テスト用の空のデータベースを用意します。
テストが成功したかどうかにかかわらず、すべてのテストの実行が終わった時点で、テストデータベースは破棄されます。
test --keepdb オプションを指定すれば、テストデータベースの破棄を防ぐことができます。これにより、複数回テストを実行しても、テストデータベースを保存できます。データベースが存在しないときは、最初に新しく作成され、そしてデータベースが最新の状態になるように、マイグレーションが順番に実行されます。
前のセクションで説明したように、テストの実行が強制的に中断された場合、テストデータベースが破棄されない可能性があります。次の実行では、データベースを再利用するか破棄するかを尋ねられるでしょう。test --noinput オプションを使用すると、プロンプトを抑制し、データベースを自動的に破棄できます。このオプションは、たとえばタイムアウトでテスト中断される可能性がある継続的インテグレーションサーバー上でのテストの実行時などに役に立ちます。
テストデータベースのデフォルトの名前は、 DATABASES 設定内の各 NAME の値の前に test_ を付けたものになります。SQLite を使っているときは、デフォルトでは、テストにはインメモリのデータベースを使います (つまり、データベースはメモリ内に作成されるため、ファイルシステムへのアクセスを完全になくすことができるのです!)。設定の DATABASES 内の TEST ディクショナリには、テストデータベースに対するいろいろな設定を書くことができます。例えば、別のデータベース名を指定したければ、 TEST ディクショナリの NAME に、 DATABASES の中から好きなデータベースを選んで指定できます。
PostgreSQL では、 USER が、ビルトインの postgres データベースへの読み取りアクセス権も持っている必要があります。
テストランナーの使うデータベースは、独立したデータベースだけでなく、設定ファイルで指定した通りのデータベースを使用させることもできます: ENGINE, USER, HOST, などです。テストデータベースは、USER で指定されたユーザによって作成されるため、そのユーザアカウントがシステム上で新しくデータベースを作成できる権限を持っている必要があります。
テストデータベースの文字エンコーディングに対するきめ細かい対応をするために、CHARSET TEST オプションを使用してください。MySQL を使用している場合は、COLLATION オプションを使用してテストデータベースが使用する特別な照合順序をコントロールできます。 これらおよびより進歩的な設定の詳細については、設定のドキュメント を参照してください。
SQLite で SQLite インメモリデータベースを使用する場合、 共有キャッシュ が有効になるため、スレッド間でデータベースを共有することが可能なテストを書くことができます。
テストの実行順序Link to this heading
すべての TestCase コードがクリーンなデータベースで実行されることを保証するために、Django のテストランナーは次の方法でテストの実行順序を決定します。
すべての
TestCaseサブクラスが最初に実行されます。そして、他のすべての Django ベースのテスト (
SimpleTestCaseをベースとしたテストケースクラス、TransactionTestCaseを含む) が、特定の順序を保証も強制もしない状態で実行されます。最後に、その他の
unittest.TestCaseテスト (doctests を含む) が実行されます。このテストの中には、データベースを変更し、そのまま元の状態に戻さないようなテストがあることもあります。
test --shuffle と --reverse オプションを使用すると、グループ内の実行順序をランダムにしたり逆にしたりすることができます。これは、テストが互いに独立していることを保証するのに役立ちます。
ロールバックのエミュレーションLink to this heading
マイグレーションで呼び出されるあらゆる初期データは、TestCase テスト内のみで有効で、 TransactionTestCase 内では無効です。加えて、トランザクションがサポートされるバックエンドのみで有効です (最も重要な例外は MyISAM です)。これは、LiveServerTestCase や StaticLiveServerTestCase といった TransactionTestCase に依存するテストに関しても同様です。
Djangoは、 TestCase や TransactionTestCase の本文で serialized_rollback オプションを True に設定することで、テストケースごとにデータを再読み込みできますが、これによりテストスイートの実行速度が約3倍遅くなることに注意してください。
サードパーティのアプリや MyISAM に対して開発する場合は、通常この設定が必要です。しかし、独自のプロジェクトをトランザクションデータベースに対して開発する場合は、ほとんどのテストで TestCase を使用するはずです。したがって、この設定は不要です。
初期シリアライズは通常、非常に高速ですが、このプロセスからいくつかのアプリを除外してテストの実行を少しでも高速にしたい場合は、それらのアプリを TEST_NON_SERIALIZED_APPS に追加します。
シリアライズされたデータが二度読み込まれるのを防ぐために、 serialized_rollback=True を設定すると、テストデータベースを flush するときに post_migrate シグナルを無効にします。
その他のテストに関する条件Link to this heading
設定ファイルで指定した DEBUG の値にかかわらず、Django のテストは DEBUG=False を指定したものとして実行されます。これは、表示されるコードの出力が、実際の環境設定で見られるものと同じになるようにするためです。
各テスト後にキャッシュはクリアされません。そのため、manage.py test fooapp を実行すると、テストで挿入されたデータが実働環境のシステムのキャッシュに残り続ける可能性があります。データベースの場合と違い、別の「テスト用のキャッシュ」が使われないためです。ただし、この動作は将来 変更される可能性があります 。
テストの出力を理解するLink to this heading
テストを実行すると、テストランナーが用意したたくさんのメッセージが表示されます。表示するメッセージの詳細レベルは、コマンドラインで verbosity オプションを指定することで、自由にコントロールできます。
Creating test database...
Creating table myapp_animal
Creating table myapp_mineral
このメッセージは、テストランナーが前のセクションで説明したテスト用のデータベースを作成していることを表しています。
テスト用データベースが作成されると、Django はテストを実行します。すべてのテストが成功すれば、次のようなメッセージが表示されるはずです。
----------------------------------------------------------------------
Ran 22 tests in 0.221s
OK
しかし、もしテストが失敗した場合には、失敗したテストとその詳細が表示されます。
======================================================================
FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/dev/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll
self.assertIs(future_poll.was_published_recently(), False)
AssertionError: True is not False
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (failures=1)
このエラー出力の詳しい説明は、このドキュメントの範囲外ですが、極めて直感的に理解できるものです。詳しく知りたければ、Python の unittest ライブラリのドキュメントを読んでみてください。
失敗したテストの数に関わらず(失敗の原因がエラー、アサーションの失敗、予期しない成功のいずれであっても)、テストランナーのスクリプトから返ってくる終了コードは 1 であることに注意してください。すべてのテストが成功すれば、0 が返ります。この特徴は、別のシェルスクリプトの中でテストランナースクリプトを実行するときに、成功したかどうかの情報が必要な時に役に立ちます。
テストのスピードアップLink to this heading
テストの並列実行Link to this heading
各テストが適切に独立性を保ったものであれば、マルチコアのハードウェア上でテストを並列実行することでスピートアップさせることができます。詳しくは test --parallel を読んでください。
パスワードのハッシュ生成Link to this heading
デフォルトのパスワードのハッシュ生成器は、設計上、時間のかかるものになっています。テストの中で多数のユーザーを認証する必要がある場合、カスタムの設定ファイルを用意して、PASSWORD_HASHERS 設定に、より高速なハッシュ生成アルゴリズムを設定すると良いでしょう。
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.MD5PasswordHasher",
]
PASSWORD_HASHERS には、必要なハッシュアルゴリズムが複数あっても、追加しておくことを忘れないようにしてください。
テストデータベースを保存するLink to this heading
test --keepdb オプションで、テスト間でテストデータベースを保存できます。テスト実行の際、データベース作成および破棄にかかる時間を大幅に短縮できます。
メディアファイルに対するディスクアクセスを回避するLink to this heading
InMemoryStorage は、メディアファイルに対するディスクアクセスを避けるための便利な手段です。すべてのデータはメモリ内にとどまり、テスト実行の後に破棄されます。
Using setUpTestData to create test dataLink to this heading
Moving common test data to TestCase.setUpTestData() will reduce the
number of times your tests need to create the data. Using
TestCase.setUpTestData() will create test data
once per class. While TestCase.setUp() will
create the test data once per test.