この記事について
PythonのWebアプリケーションフレームワーク「Django」の自動テスト機能についてまとめてみました。
公式サイトのテスト関連ページ
テストを書く場所
manage.py startapp
実行時に作成されるtests.py
に記述します。本格的なプロジェクトではtests
をパッケージ化し、その下にtest_XXX.pyという名前でテストを分割するのが一般的です。unittest
の規約にのっとり、プロジェクト内のファイル名がtest
で始まるファイルが自動テストの対象となります。
テストの実行方法
Djangoでは管理コマンドmanage.py
より自動テストが実行できる。
コマンド
manage.py test
ヘルプ表示
manage.py help test
usage: manage.py test [-h] [--version] [-v {0,1,2,3}] [--settings SETTINGS]
[--pythonpath PYTHONPATH] [--traceback] [--no-color]
[--noinput] [--failfast] [--testrunner TESTRUNNER]
[-t TOP_LEVEL] [-p PATTERN] [-k] [-r] [--debug-mode]
[-d] [--parallel [N]] [--tag TAGS]
[--exclude-tag EXCLUDE_TAGS]
[test_label [test_label ...]]
コマンドのオプション
-
-v {0,1,2,3} --verbosity {0,1,2,3}
実行ログの詳細レベル。レベル3にするとデータベースの変更タイミング等がわかるので、最初の内はこのオプションを使うと理解が早い。 -
-k --keepdb
自動テストのデフォルト動作では、毎回データベースを再作成する。このオプションを使うと再作成を回避してデータベースを削除せずに残し、次回実行時に再利用する。 -
--settings SETTINGS
単一の設定ファイル(settings.py)のままだと本番環境、開発環境、自動テスト環境を個別に設定することが難しいので、自動テスト用に個別に設定ファイルを作成するのがベストプラクティスとされている。設定ファイルの分割方法については こちら を参照。データベースの設定方法は後述。
実行範囲の指定
自動テストでは実行単位を設定できる。
# アプリケーション単位
$ ./manage.py test app
# ファイル単位
$ ./manage.py test app.tests
# テストクラス単位
$ ./manage.py test animals.tests.testcase
# メソッド単位
$ ./manage.py test animals.tests.AnimalTestCase.test_can_something
テストのデバッグ方法
pycharmなら自動テストをデバッグモードで実行できる。
参考:Creating Run/Debug Configuration for Tests
テストの動作
処理の流れ
テストケースクラス
Djangoの自動テストはPython標準のテストモジュールunittest
機能を拡張したものである。テストケースはunittest.TestCase
の継承クラスであるdjango.test.TestCase
を継承して記述する。unittest.TestCase
との違いは以下の通り。
-
テストデータの準備にフィクスチャファイルとデータ準備メソッド(setUpTestData)の2つが使える。各処理はテストケースの実行前に一度だけ実行される。
-
各テストケースはトランザクションで実行され最終的にロールバックされる。また、各テストメソッドも前述のトランザクション内の子トランザクションとして実行され、メソッドごとにロールバックされる。これによりテストケース、テストメソッド毎の独立性が担保される。
データベースの設定
settings.py上での自動テストのデータベース設定です。開発環境と同じデータベースサーバに別のデータベースを作成することを前提としています。テスト用の設定ファイルを単独で用意する場合は若干違和感のある設定となります。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': '',
'USER': 'username',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
'TEST': {
'NAME': 'test_database_name',
}
}
}
なお、TEST.NAMEでテスト用データベース名を指定しなかった場合のデフォルトは以下の通りです。
SQLite3の場合 :インメモリデータベースを作成(ファイルは作成されない)
SQLite3以外の場合:メインのデータベース名に「test_」をプレフィックスした名前
注意:デフォルトの動作ではテスト実行の度にデータベースの作成と削除を行います。よって接続ユーザーにデータベースの作成・削除権限が必要です。--keepdbオプションを使って運用するなら読み取り権限だけでOKです。
参考:テストデータをコミットさせる方法
自動テストの開発時やマルチスレッド処理などの特殊なテストを行う際に、データベースをロールバックさせずにコミットさせたい場合があります。そのような時は、「django.test.TestCase」ではなく「unittest.TestCase」を使えばOKです。
Djangoの自動テストの実行対象は「unittest.TestCase」を継承した全てのクラスなので、混在していても大丈夫です。「django.test.TestCase」の継承クラスを実行したあとで、「unittest.TestCase」の継承クラスがまとめて実行されるように順番が制御されています。
テストの書き方・ライブラリの紹介
テストデータの作成
テストデータにはフィクスチャファイルが利用できますが、以下の欠点があります。
- データの場所がテストコードと違うファイルになり確認しずらい。
- テキストデータはIDEのサポートが得られにくく管理が大変。
というわけで、テストデータはfactory-boy
というライブラリを使い、テストコード上で定義したほうが合理的とされています。factory-boy
はテストデータの値を具体値ではなくロジックとして記述することもできるので、複雑な値や構造を持つデータもフィクスチャよりも簡単に作成できます。
参考リンク:
- Github factory_boy
- くろねこ日記 テストデータ作成に便利なFactoryBoyをつかってみた
- Python: factory-boy でテストに使うオブジェクトを作る
- factory-boyとDjangoでテストを書くときにファイルをうまく使う
- FileFieldにダミーデータを設定する方法を紹介
カバレッジの計測
カバレッジ(テストのコード網羅率)の計測には「Coverage.py」を使います。
以前はdjango-nose
、django-coverage
といったパッケージが利用されましたが、現在は公式でも「Coverage.py」を推奨しています。
- [Github Coverage.py] (https://github.com/nedbat/coveragepy/blob/coverage-4.5.1a/doc/index.rst)
- Django公式: Integration with coverage.py
- もた日記 Djangoメモ(26) : coverage.pyでカバレッジ(網羅率)を計測
- Qiita:Coverage.pyでDjangoアプリケーションのカバレッジを計測する
使い方は簡単で、pip install coverage
でインストール後、Coverage.py経由でテストを実行して結果ファイルを作成し、レポートをコンソールで表示したり、HTMLで出力させたりします。除外対象の設定等はリンク先を参照してください。
# テスト実行
coverage run --source='.' manage.py test myapp
# レポート出力
coverage report
# HTML出力
coverage html
ユニットテスト
ユニットテストは python の標準的な unittest の書き方を参考にしてください。
とりあえず以下の記事がよく整理されています。
以下の記事も参考になりました。
統合テスト
Djangoのテストフレームワークにはdjango.test.Client
というモジュールが用意されています。これを使うとDjangoアプリケーションに対して仮想的なHTTPリクエストを送信し、レスポンスを受け取るとることができます。Webサーバを起動せずにテストできるので、実際のブラウザを使ったUIテストより低コストです(ただしJavaScriptの実行は不可)。
しかしRuby On Rails
で同様の役割を担うCapybara
では、Djangoのテストクライアントにはない以下の機能があり、テストの自由度に不満がでます。
- CSSセレクタを使って特定のDOMオブジェクトを取得する。
- aタグをクリックしてハイパーリンク先に遷移する。
そこでお薦めするのがライブラリdjango-webtest
です。
pythonのライブラリwebtest
をDjangoで使えるようにしたものです。
これを使うとレスポンスのHTMLページを、スクレイピングで利用されているBeautiful Soup
のオブジェクトとして扱えるので、CSSセレクタやaタグのハイパーリンク先への遷移が簡単にできるようになります。
参考:
- Github:django-webtest
- webtest:Testing Applications with WebTest
- メモ的な思考的な:Pythonで、WebTestを使って、WSGIサーバを起動せずにWSGIアプリのテストをする
- Qiita:PythonとBeautiful Soupでスクレイピング
UIテスト
DjangoでもSeleniumを使ったUIテストが可能です。DjangoではSeleniumを使ったテスト用のモジュールを用意しています。公式サイトを参考にテストケースを作成してください。
以上です。