テストツール
Djangoは、テストを作成するときに役立つツールの小さなセットを提供します。
テストクライアント
テストクライアントは、ダミーのWebブラウザーとして機能するPythonクラスであり、ビューをテストし、Djangoを利用したアプリケーションとプログラムで対話できます。
テストクライアントでできることのいくつかは次のとおりです。
- URLでGETおよびPOSTリクエストをシミュレートし、低レベルのHTTP(結果ヘッダーとステータスコード)からページコンテンツまで、すべての応答を観察します。
- リダイレクトのチェーン(存在する場合)を確認し、各ステップでURLとステータスコードを確認します。
- 特定の値を含むテンプレートコンテキストを使用して、特定のリクエストが特定のDjangoテンプレートによってレンダリングされることをテストします。
テストクライアントは、 Selenium またはその他の「ブラウザ内」フレームワークの代わりになることを意図していないことに注意してください。 Djangoのテストクライアントは別の焦点を持っています。 要するに:
- Djangoのテストクライアントを使用して、正しいテンプレートがレンダリングされていること、およびテンプレートに正しいコンテキストデータが渡されていることを確認します。
- Selenium などのブラウザー内フレームワークを使用して、レンダリングされた HTMLおよびWebページの動作、つまりJavaScript機能をテストします。 Djangoは、これらのフレームワークに特別なサポートも提供します。 詳細については、 LiveServerTestCase のセクションを参照してください。
包括的なテストスイートでは、両方のテストタイプを組み合わせて使用する必要があります。
概要と簡単な例
テストクライアントを使用するには、django.test.Client
をインスタンス化し、Webページを取得します。
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
b'<!DOCTYPE html...'
この例が示すように、Pythonインタラクティブインタープリターのセッション内からClient
をインスタンス化できます。
テストクライアントの動作に関するいくつかの重要な点に注意してください。
テストクライアントは、Webサーバーが実行されていることを必要としません。 実際、Webサーバーがまったく実行されていなくても、問題なく実行されます。 これは、HTTPのオーバーヘッドを回避し、Djangoフレームワークを直接処理するためです。 これにより、単体テストをすばやく実行できます。
ページを取得するときは、ドメイン全体ではなく、URLのパスを指定することを忘れないでください。 たとえば、これは正しいです。
>>> c.get('/login/')
これは正しくありません:
>>> c.get('https://www.example.com/login/')
テストクライアントは、Djangoプロジェクトを利用していないWebページを取得することはできません。 他のWebページを取得する必要がある場合は、
urllib
などのPython標準ライブラリモジュールを使用してください。URLを解決するために、テストクライアントは:setting: `ROOT_URLCONF` 設定によってポイントされたURLconfを使用します。
上記の例はPythonインタラクティブインタープリターで機能しますが、テストクライアントの機能の一部、特にテンプレート関連の機能は、テストの実行中でのみ使用できます。
この理由は、Djangoのテストランナーが、特定のビューによってロードされたテンプレートを判別するために、少し黒魔術を実行するためです。 この黒魔術(基本的にはメモリ内のDjangoのテンプレートシステムのパッチ)は、テストの実行中にのみ発生します。
デフォルトでは、テストクライアントはサイトによって実行されるCSRFチェックを無効にします。
何らかの理由で、テストクライアントにCSRFチェックを実行させたい必要がある場合は、CSRFチェックを実施するテストクライアントのインスタンスを作成できます。 これを行うには、クライアントを構築するときに
enforce_csrf_checks
引数を渡します。>>> from django.test import Client >>> csrf_client = Client(enforce_csrf_checks=True)
リクエストする
django.test.Client
クラスを使用してリクエストを行います。
- class Client(enforce_csrf_checks=False, json_encoder=DjangoJSONEncoder, **defaults)
構築時に引数は必要ありません。 ただし、keywords引数を使用して、いくつかのデフォルトヘッダーを指定できます。 たとえば、これにより、各リクエストで
User-Agent
HTTPヘッダーが送信されます。>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
get()、 post()などに渡される
extra
キーワード引数の値。 クラスコンストラクタに渡されるデフォルトよりも優先されます。enforce_csrf_checks
引数は、CSRF保護をテストするために使用できます(上記を参照)。json_encoder
引数を使用すると、 post()で説明されているJSONシリアル化用のカスタムJSONエンコーダーを設定できます。raise_request_exception
引数を使用すると、要求中に発生した例外をテストでも発生させるかどうかを制御できます。 デフォルトはTrue
です。バージョン3.0の新機能:
raise_request_exception
引数が追加されました。Client
インスタンスを作成したら、次のいずれかのメソッドを呼び出すことができます。- get(path, data=None, follow=False, secure=False, **extra)
提供された
path
に対してGET要求を行い、Response
オブジェクトを返します。これについては以下で説明します。data
ディクショナリのキーと値のペアは、GETデータペイロードを作成するために使用されます。 例えば:>>> c = Client() >>> c.get('/customers/details/', {'name': 'fred', 'age': 7})
…次と同等のGETリクエストの評価になります。
/customers/details/?name=fred&age=7
extra
キーワードargumentsパラメーターを使用して、リクエストで送信されるヘッダーを指定できます。 例えば:>>> c = Client() >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, ... HTTP_X_REQUESTED_WITH='XMLHttpRequest')
…HTTPヘッダー
HTTP_X_REQUESTED_WITH
を詳細ビューに送信します。これは、 django.http.HttpRequest.is_ajax()メソッドを使用するコードパスをテストするための良い方法です。CGI仕様
**extra
を介して送信されるヘッダーは、 CGI 仕様に従う必要があります。 たとえば、ブラウザからサーバーにHTTPリクエストで送信される別の「ホスト」ヘッダーをエミュレートする場合は、HTTP_HOST
として渡す必要があります。すでにURLエンコード形式のGET引数がある場合は、データ引数を使用する代わりにそのエンコードを使用できます。 たとえば、前のGETリクエストは次のように提示することもできます。
>>> c = Client() >>> c.get('/customers/details/?name=fred&age=7')
エンコードされたGETデータとデータ引数の両方を含むURLを指定すると、データ引数が優先されます。
follow
をTrue
に設定すると、クライアントはすべてのリダイレクトに従い、redirect_chain
属性が中間URLとステータスコードのタプルを含む応答オブジェクトに設定されます。/next/
にリダイレクトされたURL/redirect_me/
、/final/
にリダイレクトされたURLがある場合、次のように表示されます。>>> response = c.get('/redirect_me/', follow=True) >>> response.redirect_chain [('http://testserver/next/', 302), ('http://testserver/final/', 302)]
secure
をTrue
に設定すると、クライアントはHTTPSリクエストをエミュレートします。
- post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
提供された
path
に対してPOSTリクエストを行い、Response
オブジェクトを返します。これについては以下で説明します。data
ディクショナリのキーと値のペアは、POSTデータを送信するために使用されます。 例えば:>>> c = Client() >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
…次のURLへのPOSTリクエストが評価されます。
/login/
…このPOSTデータを使用して:
name=fred&passwd=secret
content_type
を application / json として指定した場合、data
は、dict、リスト、またはタプルの場合、json.dumps()
を使用してシリアル化されます。 シリアル化はデフォルトで DjangoJSONEncoder を使用して実行され、 Client にjson_encoder
引数を指定することでオーバーライドできます。 このシリアル化は、 put()、 patch()、および delete()要求でも発生します。バージョン2.2で変更: JSONシリアル化が拡張され、リストとタプルがサポートされるようになりました。 古いバージョンでは、dictのみがシリアル化されます。
他の
content_type
を提供する場合(例: text / xml (XMLペイロードの場合))、data
の内容は、HTTPContent-Type
のcontent_type
を使用してPOSTリクエストでそのまま送信されます。ヘッダ。content_type
の値を指定しない場合、data
の値は multipart / form-data のコンテンツタイプで送信されます。 この場合、data
のキーと値のペアはマルチパートメッセージとしてエンコードされ、POSTデータペイロードの作成に使用されます。特定のキーに複数の値を送信するには(たとえば、
<select multiple>
の選択を指定するには)、必要なキーのリストまたはタプルとして値を指定します。 たとえば、このdata
の値は、choices
という名前のフィールドに対して3つの選択された値を送信します。{'choices': ('a', 'b', 'd')}
ファイルの送信は特殊なケースです。 ファイルをPOSTするには、ファイルフィールド名をキーとして指定し、アップロードするファイルへのファイルハンドルを値として指定するだけです。 例えば:
>>> c = Client() >>> with open('wishlist.doc') as fp: ... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
(ここでの
attachment
という名前は関係ありません。ファイル処理コードが予期する名前を使用してください。)ファイルハンドルとして、ファイルのようなオブジェクト(
StringIO
やBytesIO
など)を指定することもできます。 ImageField にアップロードする場合、オブジェクトには validate_image_file_extension バリデーターを渡すname
属性が必要です。 例えば:>>> from io import BytesIO >>> img = BytesIO(b'mybinarydata') >>> img.name = 'myimage.jpg'
複数の
post()
呼び出しに同じファイルハンドルを使用する場合は、投稿間のファイルポインタを手動でリセットする必要があることに注意してください。 これを行う最も簡単な方法は、上記のように、post()
に提供された後、ファイルを手動で閉じることです。また、データを読み取れる方法でファイルが開かれていることを確認する必要があります。 ファイルに画像などのバイナリデータが含まれている場合は、
rb
(バイナリの読み取り)モードでファイルを開く必要があることを意味します。extra
引数は、 Client.get()の場合と同じように機能します。POSTでリクエストするURLにエンコードされたパラメータが含まれている場合、これらのパラメータはrequest.GETデータで利用できるようになります。 たとえば、リクエストを行う場合:
>>> c.post('/login/?visitor=true', {'name': 'fred', 'passwd': 'secret'})
…このリクエストを処理するビューは、request.POSTに問い合わせてユーザー名とパスワードを取得し、request.GETに問い合わせてユーザーが訪問者であるかどうかを判断できます。
follow
をTrue
に設定すると、クライアントはすべてのリダイレクトに従い、redirect_chain
属性が中間URLとステータスコードのタプルを含む応答オブジェクトに設定されます。secure
をTrue
に設定すると、クライアントはHTTPSリクエストをエミュレートします。
- head(path, data=None, follow=False, secure=False, **extra)
指定された
path
に対してHEAD要求を行い、Response
オブジェクトを返します。 このメソッドは、メッセージ本文を返さないことを除いて、follow
、secure
、およびextra
引数を含め、 Client.get()と同じように機能します。
- options(path, data=, content_type='application/octet-stream', follow=False, secure=False, **extra)
指定された
path
に対してOPTIONS要求を行い、Response
オブジェクトを返します。 RESTfulインターフェースのテストに役立ちます。data
を指定すると、リクエストの本文として使用され、Content-Type
ヘッダーがcontent_type
に設定されます。follow
、secure
、およびextra
引数は、 Client.get()の場合と同じように機能します。
- put(path, data=, content_type='application/octet-stream', follow=False, secure=False, **extra)
指定された
path
に対してPUT要求を行い、Response
オブジェクトを返します。 RESTfulインターフェースのテストに役立ちます。data
を指定すると、リクエストの本文として使用され、Content-Type
ヘッダーがcontent_type
に設定されます。follow
、secure
、およびextra
引数は、 Client.get()の場合と同じように機能します。
- patch(path, data=, content_type='application/octet-stream', follow=False, secure=False, **extra)
指定された
path
に対してPATCH要求を行い、Response
オブジェクトを返します。 RESTfulインターフェースのテストに役立ちます。follow
、secure
、およびextra
引数は、 Client.get()の場合と同じように機能します。
- delete(path, data=, content_type='application/octet-stream', follow=False, secure=False, **extra)
指定された
path
に対してDELETE要求を行い、Response
オブジェクトを返します。 RESTfulインターフェースのテストに役立ちます。data
を指定すると、リクエストの本文として使用され、Content-Type
ヘッダーがcontent_type
に設定されます。follow
、secure
、およびextra
引数は、 Client.get()の場合と同じように機能します。
- trace(path, follow=False, secure=False, **extra)
指定された
path
に対してTRACE要求を行い、Response
オブジェクトを返します。 診断プローブのシミュレーションに役立ちます。他の要求方法とは異なり、
data
は、 RFC 7231#section-4.3.8 に準拠するためのキーワード・パラメーターとして提供されていません。体を持ってはいけません。follow
、secure
、およびextra
引数は、 Client.get()の場合と同じように機能します。
- login(**credentials)
サイトでDjangoの認証システムを使用していて、ユーザーのログインを処理している場合は、テストクライアントの
login()
メソッドを使用して、サイトにログインしているユーザーの効果をシミュレートできます。このメソッドを呼び出すと、テストクライアントには、ビューの一部を形成する可能性のあるログインベースのテストに合格するために必要なすべてのCookieとセッションデータが含まれます。
credentials
引数の形式は、使用している認証バックエンド(:setting: `AUTHENTICATION_BACKENDS` 設定で構成されます)によって異なります。 Djangoが提供する標準の認証バックエンド(ModelBackend
)を使用している場合、credentials
は、キーワード引数として提供されるユーザーのユーザー名とパスワードである必要があります。>>> c = Client() >>> c.login(username='fred', password='secret') # Now you can access a view that's only available to logged-in users.
別の認証バックエンドを使用している場合、この方法では別の認証情報が必要になる場合があります。 バックエンドの
authenticate()
メソッドで必要な資格情報が必要です。login()
は、資格情報が受け入れられ、ログインが成功した場合、True
を返します。最後に、この方法を使用する前に、ユーザーアカウントを作成することを忘れないでください。 上で説明したように、テストランナーは、デフォルトでユーザーを含まないテストデータベースを使用して実行されます。 その結果、本番サイトで有効なユーザーアカウントは、テスト条件下では機能しません。 テストスイートの一部として、手動(DjangoモデルAPIを使用)またはテストフィクスチャを使用してユーザーを作成する必要があります。 テストユーザーにパスワードを設定する場合は、password属性を直接設定してユーザーのパスワードを設定することはできません。正しくハッシュされたパスワードを保存するには、 set_password()関数を使用する必要があります。 または、 create_user()ヘルパーメソッドを使用して、正しくハッシュされたパスワードで新しいユーザーを作成することもできます。
- force_login(user, backend=None)
サイトでDjangoの認証システムを使用している場合は、
force_login()
メソッドを使用して、サイトにログインしているユーザーの効果をシミュレートできます。 テストでユーザーのログインが必要であり、ユーザーのログイン方法の詳細が重要でない場合は、 login()の代わりにこのメソッドを使用してください。login()
とは異なり、このメソッドは認証と検証の手順をスキップします。非アクティブなユーザー( is_active = False )はログインを許可され、ユーザーの資格情報を提供する必要はありません。ユーザーは、
backend
属性をbackend
引数の値(点線のPythonパス文字列である必要があります)に設定するか、値がそうでない場合はsettings.AUTHENTICATION_BACKENDS[0]
に設定します。提供された。 login()によって呼び出される authenticate()関数は、通常、このようにユーザーに注釈を付けます。この方法は、高価なパスワードハッシュアルゴリズムがバイパスされるため、
login()
よりも高速です。 また、のテスト中に、より弱いハッシャーを使用してすることで、login()
を高速化できます。
- logout()
サイトでDjangoの認証システムを使用している場合は、
logout()
メソッドを使用して、ユーザーがサイトからログアウトした場合の影響をシミュレートできます。このメソッドを呼び出すと、テストクライアントはすべてのCookieとセッションデータをデフォルトにクリアします。 後続のリクエストは、 AnonymousUser から送信されたように見えます。
応答のテスト
get()
メソッドとpost()
メソッドはどちらもResponse
オブジェクトを返します。 このResponse
オブジェクトは、Djangoビューによって返されるHttpResponse
オブジェクトと同じではありません。 テスト応答オブジェクトには、テストコードの検証に役立つ追加データがいくつかあります。
具体的には、Response
オブジェクトには次の属性があります。
- class Response
- client
応答をもたらす要求を行うために使用されたテストクライアント。
- content
バイト文字列としての応答の本文。 これは、ビューまたはエラーメッセージによってレンダリングされた最終的なページコンテンツです。
- context
応答コンテンツを生成するテンプレートをレンダリングするために使用されたテンプレート
Context
インスタンス。レンダリングされたページが複数のテンプレートを使用している場合、
context
はレンダリングされた順序でContext
オブジェクトのリストになります。レンダリング中に使用されるテンプレートの数に関係なく、
[]
演算子を使用してコンテキスト値を取得できます。 たとえば、コンテキスト変数name
は、次を使用して取得できます。>>> response = client.get('/foo/') >>> response.context['name'] 'Arthur'
Djangoテンプレートを使用していませんか?
この属性は、 DjangoTemplates バックエンドを使用する場合にのみ入力されます。 別のテンプレートエンジンを使用している場合は、 context_data がその属性を持つ応答の適切な代替手段になる可能性があります。
- exc_info
バージョン3.0の新機能。
ビュー中に発生した未処理の例外(存在する場合)に関する情報を提供する3つの値のタプル。
値は(type、value、traceback)であり、Pythonの
sys.exc_info()
によって返されるものと同じです。 それらの意味は次のとおりです。type :例外のタイプ。
value :例外インスタンス。
traceback :例外が最初に発生したポイントでコールスタックをカプセル化するトレースバックオブジェクト。
例外が発生しなかった場合、
exc_info
はNone
になります。
- json(**kwargs)
JSONとして解析された応答の本文。 追加のキーワード引数が
json.loads()
に渡されます。 例えば:>>> response = client.get('/foo/') >>> response.json()['name'] 'Arthur'
Content-Type
ヘッダーが"application/json"
でない場合、応答を解析しようとするとValueError
が発生します。
- request
応答を刺激した要求データ。
- wsgi_request
応答を生成したテストハンドラーによって生成された
WSGIRequest
インスタンス。
- status_code
整数としての応答のHTTPステータス。 定義されたコードの完全なリストについては、 IANAステータスコードレジストリを参照してください。
- templates
最終コンテンツのレンダリングに使用される
Template
インスタンスのリスト(レンダリングされた順序)。 テンプレートがファイルからロードされた場合は、リスト内のテンプレートごとに、template.name
を使用してテンプレートのファイル名を取得します。 (名前は'admin/index.html'
などの文字列です。)Djangoテンプレートを使用していませんか?
この属性は、 DjangoTemplates バックエンドを使用する場合にのみ入力されます。 別のテンプレートエンジンを使用している場合、レンダリングに使用するテンプレートの名前だけが必要な場合は、 template_name が適切な代替手段になる可能性があります。
- resolver_match
応答の ResolverMatch のインスタンス。 たとえば、 func 属性を使用して、応答を提供したビューを確認できます。
# my_view here is a function based view self.assertEqual(response.resolver_match.func, my_view) # class-based views need to be compared by name, as the functions # generated by as_view() won't be equal self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__)
指定されたURLが見つからない場合、この属性にアクセスすると Resolver404 例外が発生します。
応答オブジェクトでディクショナリ構文を使用して、HTTPヘッダーの設定の値を照会することもできます。 たとえば、response['Content-Type']
を使用して応答のコンテンツタイプを判別できます。
例外
例外を発生させるビューをテストクライアントに向け、Client.raise_request_exception
がTrue
の場合、その例外はテストケースに表示されます。 次に、標準のtry ... except
ブロックまたはassertRaises()
を使用して、例外をテストできます。
テストクライアントに表示されない唯一の例外は、 Http404 、 PermissionDenied 、SystemExit
、および SuspiciousOperation です。 Djangoはこれらの例外を内部でキャッチし、適切なHTTP応答コードに変換します。 このような場合、テストでresponse.status_code
を確認できます。
Client.raise_request_exception
がFalse
の場合、テストクライアントはブラウザに返されるのと同じように500応答を返します。 応答には、未処理の例外に関する情報を提供する属性 exc_info があります。
永続的な状態
テストクライアントはステートフルです。 応答がCookieを返す場合、そのCookieはテストクライアントに保存され、後続のすべてのget()
およびpost()
要求とともに送信されます。
これらのCookieの有効期限ポリシーは守られていません。 Cookieの有効期限が切れる場合は、Cookieを手動で削除するか、新しいClient
インスタンスを作成します(これにより、すべてのCookieが効果的に削除されます)。
テストクライアントには、永続的な状態情報を格納する2つの属性があります。 これらのプロパティには、テスト条件の一部としてアクセスできます。
- Client.cookies
- すべてのクライアントCookieの現在の値を含むPython
SimpleCookie
オブジェクト。 詳細については、http.cookies
モジュールのドキュメントを参照してください。
- Client.session
セッション情報を含む辞書のようなオブジェクト。 詳細については、セッションドキュメントを参照してください。
セッションを変更して保存するには、最初に変数に格納する必要があります(このプロパティにアクセスするたびに新しい
SessionStore
が作成されるため)。def test_something(self): session = self.client.session session['somekey'] = 'test' session.save()
言語の設定
国際化とローカリゼーションをサポートするアプリケーションをテストするときは、テストクライアント要求の言語を設定することをお勧めします。 その方法は、 LocaleMiddleware が有効になっているかどうかによって異なります。
ミドルウェアが有効になっている場合は、:setting: `LANGUAGE_COOKIE_NAME` という名前と言語コードの値を使用してCookieを作成することにより、言語を設定できます。
from django.conf import settings
def test_language_using_cookie(self):
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'fr'})
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
または、リクエストにAccept-Language
HTTPヘッダーを含めることによって:
def test_language_using_header(self):
response = self.client.get('/', HTTP_ACCEPT_LANGUAGE='fr')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
詳細については、 Djangoが言語設定を検出する方法を参照してください。
ミドルウェアが有効になっていない場合は、 translation.override()を使用してアクティブな言語を設定できます。
from django.utils import translation
def test_language_using_override(self):
with translation.override('fr'):
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
詳細については、アクティブな言語の明示的な設定を参照してください。
例
以下は、テストクライアントを使用した単体テストです。
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
# Every test needs a client.
self.client = Client()
def test_details(self):
# Issue a GET request.
response = self.client.get('/customer/details/')
# Check that the response is 200 OK.
self.assertEqual(response.status_code, 200)
# Check that the rendered context contains 5 customers.
self.assertEqual(len(response.context['customers']), 5)
提供されたテストケースクラス
通常のPythonユニットテストクラスは、unittest.TestCase
の基本クラスを拡張します。 Djangoは、この基本クラスのいくつかの拡張機能を提供します。
[[../File:../../_images/django_unittest_classes_hierarchy|thumb|none|508x328px|alt=
| ]]通常のunittest.TestCase
を任意のサブクラスに変換できます。テストの基本クラスをunittest.TestCase
からサブクラスに変更します。 すべての標準Pythonユニットテスト機能が利用可能になり、以下の各セクションで説明するように、いくつかの便利な追加機能が追加されます。
SimpleTestCase
- class SimpleTestCase
この機能を追加するunittest.TestCase
のサブクラス:
- 次のようないくつかの有用なアサーション:
- 呼び出し可能が特定の例外を発生させることを確認します。
- 呼び出し可能が特定の警告をトリガーすることを確認します。
- フォームフィールドのテストレンダリングとエラー処理。
- 特定のフラグメントの有無について HTML応答をテストします。
- テンプレートが特定の応答コンテンツを生成するために使用されている/使用されていないことを確認します。
- 2つの URL が等しいことを確認します。
- HTTP リダイレクトの確認はアプリによって実行されます。
- 2つの HTMLフラグメントの同等性/不同等性または封じ込めを堅牢にテストします。
- 2つの XMLフラグメントの同等性/不同等性を堅牢にテストします。
- 2つの JSONフラグメントが等しいかどうかを堅牢にテストします。
- 変更された設定でテストを実行する機能。
- クライアント クライアントを使用します。
テストでデータベースクエリを実行する場合は、サブクラス TransactionTestCase または TestCase を使用します。
- SimpleTestCase.databases
バージョン2.2の新機能。
SimpleTestCase は、デフォルトでデータベースクエリを許可しません。 これは、各
SimpleTestCase
テストがトランザクションで実行されないため、他のテストに影響を与える書き込みクエリの実行を回避するのに役立ちます。 この問題を気にしない場合は、テストクラスでdatabases
クラス属性を'__all__'
に設定することで、この動作を無効にできます。
- SimpleTestCase.allow_database_queries
バージョン2.2以降非推奨。
この属性は非推奨になり、データベースが優先されます。
allow_database_queries = True
の以前の動作は、databases = '__all__'
を設定することで実現できます。
警告
SimpleTestCase
とそのサブクラス(例: TestCase
、…)は、setUpClass()
およびtearDownClass()
に依存して、クラス全体の初期化を実行します(例: 設定の上書き)。 これらのメソッドをオーバーライドする必要がある場合は、super
実装を呼び出すことを忘れないでください。
class MyTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
...
@classmethod
def tearDownClass(cls):
...
super().tearDownClass()
setUpClass()
中に例外が発生した場合は、Pythonの動作を必ず考慮してください。 その場合、クラス内のテストもtearDownClass()
も実行されません。 django.test.TestCase の場合、これによりsuper()
で作成されたトランザクションがリークし、一部のプラットフォームでのセグメンテーション違反などのさまざまな症状が発生します(macOSで報告)。 setUpClass()
でunittest.SkipTest
などの例外を意図的に発生させたい場合は、super()
を呼び出す前に必ず例外を発生させてください。
TransactionTestCase
- class TransactionTestCase
TransactionTestCase
は SimpleTestCase を継承して、データベース固有の機能をいくつか追加します。
- 各テストの開始時にデータベースを既知の状態にリセットして、ORMのテストと使用を容易にします。
- データベースフィクスチャ。
- データベースバックエンド機能に基づいてスキップをテストします。
- 残りの特殊な assert * メソッド。
Djangoの TestCase クラスは、TransactionTestCase
のより一般的に使用されるサブクラスであり、データベーストランザクション機能を利用して、各テストの開始時にデータベースを既知の状態にリセットするプロセスを高速化します。 ただし、この結果、一部のデータベースの動作はDjango TestCase
クラス内でテストできなくなります。 たとえば、 select_for_update()を使用する場合に必要なように、コードのブロックがトランザクション内で実行されていることをテストすることはできません。 そのような場合は、TransactionTestCase
を使用する必要があります。
TransactionTestCase
とTestCase
は、データベースが既知の状態にリセットされる方法と、テストコードがコミットとロールバックの効果をテストする機能を除いて同じです。
TransactionTestCase
は、テストの実行後にすべてのテーブルを切り捨ててデータベースをリセットします。TransactionTestCase
は、コミットとロールバックを呼び出し、データベースに対するこれらの呼び出しの影響を観察する場合があります。- 一方、
TestCase
は、テスト後にテーブルを切り捨てません。 代わりに、テストの最後にロールバックされるデータベーストランザクションにテストコードを含めます。 これにより、テスト終了時のロールバックによってデータベースが初期状態に復元されることが保証されます。
警告
TestCase
は、ロールバックをサポートしていないデータベースで実行されています(例: MyISAMストレージエンジンを搭載したMySQL)、およびTransactionTestCase
のすべてのインスタンスは、テストデータベースからすべてのデータを削除することにより、テストの最後にロールバックします。
アプリはデータが再読み込みされたのを認識しません; この機能が必要な場合(たとえば、サードパーティのアプリでこれを有効にする必要があります)、TestCase
本体内にserialized_rollback = True
を設定できます。
TestCase
- class TestCase
これは、Djangoでテストを作成するために使用する最も一般的なクラスです。 TransactionTestCase (および拡張機能 SimpleTestCase )から継承します。 Djangoアプリケーションがデータベースを使用しない場合は、 SimpleTestCase を使用してください。
クラス:
- 2つのネストされた atomic()ブロック内にテストをラップします。1つはクラス全体用で、もう1つは各テスト用です。 したがって、特定のデータベーストランザクションの動作をテストする場合は、 TransactionTestCase を使用してください。
- 各テストの最後に、延期可能なデータベース制約をチェックします。
また、追加の方法も提供します。
- classmethod TestCase.setUpTestData()
上記のクラスレベルの
atomic
ブロックでは、TestCase
全体に対して1回、クラスレベルで初期データを作成できます。 この手法により、setUp()
を使用する場合と比較してより高速なテストが可能になります。例えば:
from django.test import TestCase class MyTests(TestCase): @classmethod def setUpTestData(cls): # Set up data for the whole TestCase cls.foo = Foo.objects.create(bar="Test") ... def test1(self): # Some test using self.foo ... def test2(self): # Some other test using self.foo ...
トランザクションをサポートしていないデータベース(たとえば、MyISAMエンジンを搭載したMySQL)でテストを実行すると、各テストの前に
setUpTestData()
が呼び出され、速度の利点が無効になることに注意してください。テストメソッドの
setUpTestData()
で作成されたオブジェクトを変更しないように注意してください。 クラスレベルで行われたセットアップ作業からのメモリ内オブジェクトへの変更は、テストメソッド間で保持されます。 それらを変更する必要がある場合は、たとえば refresh_from_db()を使用してsetUp()
メソッドでそれらを再ロードできます。
LiveServerTestCase
- class LiveServerTestCase
LiveServerTestCase
は、基本的に TransactionTestCase と同じですが、セットアップ時にバックグラウンドでライブDjangoサーバーを起動し、ティアダウン時にシャットダウンします。 これにより、 Djangoダミークライアント以外の自動テストクライアント( Selenium クライアントなど)を使用して、ブラウザー内で一連の機能テストを実行し、実際のテストをシミュレートできます。ユーザーのアクション。
ライブサーバーはlocalhost
をリッスンし、オペレーティングシステムによって割り当てられた空きポートを使用するポート0にバインドします。 テスト中は、self.live_server_url
を使用してサーバーのURLにアクセスできます。
LiveServerTestCase
の使用方法を示すために、Seleniumテストを作成してみましょう。 まず、 seleniumパッケージをPythonパスにインストールする必要があります。
次に、LiveServerTestCase
ベースのテストをアプリのテストモジュールに追加します(例:myapp/tests.py
)。 この例では、 staticfiles アプリを使用していて、DEBUG=True
で開発時に取得するのと同様に、テストの実行中に静的ファイルを提供する必要があると想定します。 NS :djadmin: `collectstatic` を使用してそれらを収集する必要はありません。 その機能を提供する StaticLiveServerTestCase サブクラスを使用します。 不要な場合はdjango.test.LiveServerTestCase
に交換してください。
このテストのコードは次のようになります。
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver.firefox.webdriver import WebDriver
class MySeleniumTests(StaticLiveServerTestCase):
fixtures = ['user-data.json']
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.selenium = WebDriver()
cls.selenium.implicitly_wait(10)
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super().tearDownClass()
def test_login(self):
self.selenium.get('%s%s' % (self.live_server_url, '/login/'))
username_input = self.selenium.find_element_by_name("username")
username_input.send_keys('myuser')
password_input = self.selenium.find_element_by_name("password")
password_input.send_keys('secret')
self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
最後に、次のようにテストを実行できます。
この例では、Firefoxが自動的に開き、ログインページに移動し、資格情報を入力して、[ログイン]ボタンを押します。 Firefoxがインストールされていない場合、または別のブラウザーを使用したい場合に備えて、Seleniumは他のドライバーを提供します。 上記の例は、Seleniumクライアントが実行できることのほんの一部です。 詳細については、フルリファレンスをご覧ください。
ノート
インメモリSQLiteデータベースを使用してテストを実行する場合、同じデータベース接続が2つのスレッド(ライブサーバーが実行されるスレッドとテストケースが実行されるスレッド)によって並行して共有されます。 2つのスレッドによるこの共有接続を介したデータベースクエリの同時発生を防ぐことが重要です。これにより、テストがランダムに失敗する場合があります。 したがって、2つのスレッドが同時にデータベースにアクセスしないようにする必要があります。 特に、これは、場合によっては(たとえば、リンクをクリックした後やフォームを送信した直後)、Seleniumが応答を受信し、次のページが読み込まれたことを確認してから、さらにテストを実行する必要があることを意味します。 これを行うには、たとえば、応答で<body>
HTMLタグが見つかるまでSeleniumを待機させます(Selenium> 2.13が必要)。
def test_login(self):
from selenium.webdriver.support.wait import WebDriverWait
timeout = 2
...
self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
# Wait until the response is received
WebDriverWait(self.selenium, timeout).until(
lambda driver: driver.find_element_by_tag_name('body'))
ここで注意が必要なのは、特にサーバーが最初のドキュメントを生成した後にHTMLを動的に生成する最新のWebアプリでは、「ページの読み込み」などが実際にはないことです。 したがって、応答に<body>
が存在するかどうかを確認することは、必ずしもすべてのユースケースに適しているとは限りません。 詳細については、 Selenium FAQ および Seleniumドキュメントを参照してください。
テストケースの機能
デフォルトのテストクライアント
- SimpleTestCase.client
django.test.*TestCase
インスタンスのすべてのテストケースは、Djangoテストクライアントのインスタンスにアクセスできます。 このクライアントには、self.client
としてアクセスできます。 このクライアントはテストごとに再作成されるため、あるテストから別のテストに持ち越される状態(Cookieなど)について心配する必要はありません。
これは、各テストでClient
をインスタンス化する代わりに、次のことを意味します。
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def test_details(self):
client = Client()
response = client.get('/customer/details/')
self.assertEqual(response.status_code, 200)
def test_index(self):
client = Client()
response = client.get('/customer/index/')
self.assertEqual(response.status_code, 200)
…次のように、self.client
を参照できます。
from django.test import TestCase
class SimpleTest(TestCase):
def test_details(self):
response = self.client.get('/customer/details/')
self.assertEqual(response.status_code, 200)
def test_index(self):
response = self.client.get('/customer/index/')
self.assertEqual(response.status_code, 200)
テストクライアントのカスタマイズ
- SimpleTestCase.client_class
別のClient
クラス(たとえば、動作がカスタマイズされたサブクラス)を使用する場合は、 client_class クラス属性を使用します。
from django.test import Client, TestCase
class MyTestClient(Client):
# Specialized methods for your environment
...
class MyTest(TestCase):
client_class = MyTestClient
def test_my_stuff(self):
# Here self.client is an instance of MyTestClient...
call_some_test_code()
フィクスチャのロード
- TransactionTestCase.fixtures
データベースにデータがない場合、データベースに裏打ちされたWebサイトのテストケースはあまり役に立ちません。 TestCase.setUpTestData()など、ORMを使用してオブジェクトを作成すると、テストが読みやすく、保守しやすくなりますが、フィクスチャを使用することもできます。
フィクスチャは、Djangoがデータベースにインポートする方法を知っているデータのコレクションです。 たとえば、サイトにユーザーアカウントがある場合、テスト中にデータベースにデータを入力するために、偽のユーザーアカウントのフィクスチャを設定できます。
フィクスチャを作成する最も簡単な方法は、 :djadmin: `manage.py dumpdata ` 指図。 これは、データベースにすでにいくつかのデータがあることを前提としています。 を参照してください :djadmin: `dumpdataドキュメント ` 詳細については。
フィクスチャを作成し、:setting: `INSTALLED_APPS` のいずれかのfixtures
ディレクトリに配置したら、fixtures
クラス属性:
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
fixtures = ['mammals.json', 'birds']
def setUp(self):
# Test definitions as before.
call_setup_methods()
def test_fluffy_animals(self):
# A test that uses the fixtures.
call_some_test_code()
具体的には次のようになります。
- 各テストの開始時に、
setUp()
が実行される前に、Djangoはデータベースをフラッシュし、:djadmin: `migrate` が呼び出された直後の状態にデータベースを戻します。 - 次に、指定されたすべてのフィクスチャがインストールされます。 この例では、Djangoは
mammals
という名前のJSONフィクスチャをインストールし、その後にbirds
という名前のフィクスチャをインストールします。 フィクスチャの定義とインストールの詳細については、:djadmin: `loaddata` のドキュメントを参照してください。
パフォーマンス上の理由から、 TestCase は、各テストの前ではなく、 setUpTestData()の前に、テストクラス全体に対して1回フィクスチャをロードし、トランザクションを使用して各テストの前にデータベースをクリーンアップします。 いずれの場合も、テストの結果が別のテストやテストの実行順序の影響を受けないことを確認できます。
デフォルトでは、フィクスチャはdefault
データベースにのみロードされます。 複数のデータベースを使用していて TransactionTestCase.databases を設定している場合、フィクスチャは指定されたすべてのデータベースにロードされます。
URLconf構成
アプリケーションがビューを提供する場合は、テストクライアントを使用してそれらのビューを実行するテストを含めることができます。 ただし、エンドユーザーは、選択した任意のURLでアプリケーションにビューを自由にデプロイできます。 これは、ビューが特定のURLで利用可能になるという事実にテストが依存できないことを意味します。 URLconf構成用に@override_settings(ROOT_URLCONF=...)
でテストクラスまたはテストメソッドを装飾します。
マルチデータベースのサポート
- TransactionTestCase.databases
バージョン2.2の新機能。
Djangoは、設定の:setting: `DATABASES` 定義で定義され、databases
を介して少なくとも1つのテストによって参照されるすべてのデータベースに対応するテストデータベースをセットアップします。
ただし、Django TestCase
の実行にかかる時間の大部分は、flush
の呼び出しによって消費され、各テスト実行の開始時にクリーンなデータベースを確保します。 複数のデータベースがある場合、複数のフラッシュが必要です(データベースごとに1つ)。これは、特にテストで複数のデータベースアクティビティをテストする必要がない場合は、時間のかかるアクティビティになる可能性があります。
最適化として、Djangoは各テスト実行の開始時にdefault
データベースのみをフラッシュします。 セットアップに複数のデータベースが含まれていて、すべてのデータベースをクリーンにする必要があるテストがある場合は、テストスイートのdatabases
属性を使用して、追加のデータベースのフラッシュを要求できます。
例えば:
class TestMyViews(TransactionTestCase):
databases = {'default', 'other'}
def test_index_page_view(self):
call_some_test_code()
このテストケースは、test_index_page_view
を実行する前に、default
およびother
テストデータベースをフラッシュします。 '__all__'
を使用して、すべてのテストデータベースをフラッシュする必要があることを指定することもできます。
databases
フラグは、 TransactionTestCase.fixtures がロードされるデータベースも制御します。 デフォルトでは、フィクスチャはdefault
データベースにのみロードされます。
databases
にないデータベースに対するクエリでは、テスト間の状態リークを防ぐためにアサーションエラーが発生します。
- TransactionTestCase.multi_db
バージョン2.2以降非推奨。
この属性は非推奨になり、データベースが優先されます。 multi_db = True
の以前の動作は、databases = '__all__'
を設定することで実現できます。
- TestCase.databases
バージョン2.2の新機能。
デフォルトでは、default
データベースのみがTestCase
の実行中にトランザクションにラップされ、他のデータベースにクエリを実行しようとすると、テスト間の状態リークを防ぐためにアサーションエラーが発生します。
テストクラスのdatabases
クラス属性を使用して、default
以外のデータベースに対するトランザクションラッピングを要求します。
例えば:
class OtherDBTests(TestCase):
databases = {'other'}
def test_other_db_query(self):
...
このテストでは、other
データベースに対するクエリのみが許可されます。 SimpleTestCase.databases および TransactionTestCase.databases の場合と同様に、'__all__'
定数を使用して、テストですべてのデータベースへのクエリを許可するように指定できます。
- TestCase.multi_db
バージョン2.2以降非推奨。
この属性は非推奨になり、データベースが優先されます。 multi_db = True
の以前の動作は、databases = '__all__'
を設定することで実現できます。
設定の上書き
警告
以下の機能を使用して、テストの設定値を一時的に変更します。 django.conf.settings
を直接操作しないでください。このような操作の後、Djangoは元の値を復元しません。
- SimpleTestCase.settings()
テストの目的で、設定を一時的に変更し、テストコードの実行後に元の値に戻すと便利なことがよくあります。 このユースケースでは、Djangoは settings()と呼ばれる標準のPythonコンテキストマネージャー( PEP 343 を参照)を提供します。これは次のように使用できます。
from django.test import TestCase
class LoginTestCase(TestCase):
def test_login(self):
# First check for the default behavior
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/accounts/login/?next=/sekrit/')
# Then override the LOGIN_URL setting
with self.settings(LOGIN_URL='/other/login/'):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
この例では、with
ブロックのコードの:setting: `LOGIN_URL` 設定を上書きし、後でその値を前の状態にリセットします。
- SimpleTestCase.modify_settings()
値のリストを含む設定を再定義するのは扱いにくいことがわかります。 実際には、値を追加または削除するだけで十分なことがよくあります。 Djangoは、設定の変更を容易にするために modify_settings()コンテキストマネージャーを提供します。
from django.test import TestCase
class MiddlewareTestCase(TestCase):
def test_cache_middleware(self):
with self.modify_settings(MIDDLEWARE={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}):
response = self.client.get('/')
# ...
アクションごとに、値のリストまたは文字列を指定できます。 値がリストにすでに存在する場合、append
およびprepend
は効果がありません。 値が存在しない場合、remove
も同様です。
- override_settings()
テストメソッドの設定をオーバーライドする場合、Djangoは override_settings()デコレーターを提供します( PEP 318 を参照)。 これは次のように使用されます:
from django.test import TestCase, override_settings
class LoginTestCase(TestCase):
@override_settings(LOGIN_URL='/other/login/')
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
デコレータは、 TestCase クラスにも適用できます。
from django.test import TestCase, override_settings
@override_settings(LOGIN_URL='/other/login/')
class LoginTestCase(TestCase):
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
- modify_settings()
同様に、Djangoは modify_settings()デコレータを提供します。
from django.test import TestCase, modify_settings
class MiddlewareTestCase(TestCase):
@modify_settings(MIDDLEWARE={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
})
def test_cache_middleware(self):
response = self.client.get('/')
# ...
デコレータは、テストケースクラスにも適用できます。
from django.test import TestCase, modify_settings
@modify_settings(MIDDLEWARE={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
})
class MiddlewareTestCase(TestCase):
def test_cache_middleware(self):
response = self.client.get('/')
# ...
ノート
クラスが与えられると、これらのデコレータはクラスを直接変更して返します。 変更されたコピーを作成して返すことはありません。 したがって、上記の例を微調整して戻り値をLoginTestCase
またはMiddlewareTestCase
とは異なる名前に割り当てようとすると、元のテストケースクラスが引き続き同じように影響を受けることに驚かれるかもしれません。デコレータ。 特定のクラスでは、 modify_settings()は常に override_settings()の後に適用されます。
警告
設定ファイルには、Django内部の初期化中にのみ参照されるいくつかの設定が含まれています。 override_settings
で変更した場合、django.conf.settings
モジュールでアクセスすると設定が変更されますが、Djangoの内部ではアクセス方法が異なります。 事実上、これらの設定で override_settings()または modify_settings()を使用しても、期待どおりの動作が得られない可能性があります。
:setting: `DATABASES` 設定を変更することはお勧めしません。 :setting: `CACHES` 設定を変更することは可能ですが、 django.contrib.sessions のように、キャッシュを利用する内部を使用している場合は少し注意が必要です。 たとえば、キャッシュされたセッションを使用し、:setting: `CACHES` をオーバーライドするテストでセッションバックエンドを再初期化する必要があります。
最後に、override_settings()
はモジュールが最初にインポートされたときにのみ評価されるため、このような値では機能しないため、設定をモジュールレベルの定数としてエイリアスすることは避けてください。
次のように、設定が上書きされた後に設定を削除することで、設定がないことをシミュレートすることもできます。
@override_settings()
def test_something(self):
del settings.LOGIN_URL
...
設定を上書きするときは、アプリのコードが、設定が変更されても状態を保持するキャッシュまたは同様の機能を使用する場合を必ず処理してください。 Djangoは django.test.signals.setting_changed シグナルを提供します。これにより、コールバックを登録してクリーンアップするか、設定が変更されたときに状態をリセットできます。
Django自体がこの信号を使用して、さまざまなデータをリセットします。
オーバーライドされた設定 | データのリセット |
---|---|
USE_TZ、TIME_ZONE | データベースのタイムゾーン |
テンプレート | テンプレートエンジン |
SERIALIZATION_MODULES | シリアライザーキャッシュ |
LOCALE_PATHS、LANGUAGE_CODE | デフォルトの翻訳とロードされた翻訳 |
MEDIA_ROOT、DEFAULT_FILE_STORAGE | デフォルトのファイルストレージ |
テスト送信トレイを空にする
DjangoのカスタムTestCase
クラスのいずれかを使用する場合、テストランナーは、各テストケースの開始時にテストメールの送信トレイの内容をクリアします。
テスト中のメールサービスの詳細については、以下のメールサービスを参照してください。
アサーション
Pythonの通常のunittest.TestCase
クラスはassertTrue()
やassertEqual()
などのアサーションメソッドを実装しているため、Djangoのカスタム TestCase クラスは、 Webアプリケーションのテスト:
これらのアサーションメソッドのほとんどによって表示される失敗メッセージは、msg_prefix
引数を使用してカスタマイズできます。 この文字列は、アサーションによって生成された失敗メッセージの前に付けられます。 これにより、テストスイートの場所と失敗の原因を特定するのに役立つ可能性のある追加の詳細を提供できます。
- SimpleTestCase.assertRaisesMessage(expected_exception, expected_message, callable, *args, **kwargs)
SimpleTestCase.assertRaisesMessage(expected_exception, expected_message) callable
を実行するとexpected_exception
が発生し、expected_message
が例外のメッセージに含まれていることを表明します。 その他の結果は失敗として報告されます。 これはunittest.TestCase.assertRaisesRegex()
のより単純なバージョンですが、expected_message
は正規表現として扱われないという違いがあります。expected_exception
およびexpected_message
パラメーターのみが指定されている場合は、コンテキストマネージャーを返し、テスト対象のコードを関数としてではなくインラインで記述できるようにします。with self.assertRaisesMessage(ValueError, 'invalid literal for int()'): int('a')
- SimpleTestCase.assertWarnsMessage(expected_warning, expected_message, callable, *args, **kwargs)
SimpleTestCase.assertWarnsMessage(expected_warning, expected_message)
- SimpleTestCase.assertRaisesMessage()に類似していますが、
assertRaisesRegex()
ではなくassertWarnsRegex()
用です。
- SimpleTestCase.assertFieldOutput(fieldclass, valid, invalid, field_args=None, field_kwargs=None, empty_value=)
フォームフィールドがさまざまな入力で正しく動作することを表明します。
- パラメーター
fieldclass –テストするフィールドのクラス。
valid –有効な入力を期待されるクリーンアップされた値にマッピングする辞書。
invalid –無効な入力を1つ以上の発生したエラーメッセージにマッピングする辞書。
field_args –フィールドをインスタンス化するために渡される引数。
field_kwargs –フィールドをインスタンス化するために渡されるkwargs。
empty_value –
empty_values
の入力に対して期待されるクリーンな出力。
たとえば、次のコードは、
EmailField
が[email protected]
を有効な電子メールアドレスとして受け入れるが、aaa
を適切なエラーメッセージで拒否することをテストします。self.assertFieldOutput(EmailField, {'[email protected]': '[email protected]'}, {'aaa': ['Enter a valid email address.']})
- SimpleTestCase.assertFormError(response, form, field, errors, msg_prefix=)
フォーム上のフィールドがフォームにレンダリングされたときに、提供されたエラーのリストを表示することを表明します。
form
は、Form
インスタンスがテンプレートコンテキストで付けられた名前です。field
は、チェックするフォームのフィールドの名前です。field
の値がNone
の場合、フィールド以外のエラー( form.non_field_errors()からアクセスできるエラー)がチェックされます。errors
は、フォーム検証の結果として予期されるエラー文字列またはエラー文字列のリストです。
- SimpleTestCase.assertFormsetError(response, formset, form_index, field, errors, msg_prefix=)
formset
がレンダリングされると、提供されたエラーのリストが発生することを表明します。formset
は、Formset
インスタンスがテンプレートコンテキストで付けられた名前です。form_index
は、Formset
内のフォームの番号です。form_index
の値がNone
の場合、非フォームエラー(formset.non_form_errors()
を介してアクセスできるエラー)がチェックされます。field
は、チェックするフォームのフィールドの名前です。field
の値がNone
の場合、フィールド以外のエラー( form.non_field_errors()からアクセスできるエラー)がチェックされます。errors
は、フォーム検証の結果として予期されるエラー文字列またはエラー文字列のリストです。
- SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix=, html=False)
Response
インスタンスが指定されたstatus_code
を生成し、text
が応答のコンテンツに表示されることを表明します。count
が指定されている場合、text
は応答で正確にcount
回発生する必要があります。html
をTrue
に設定して、text
をHTMLとして処理します。 応答コンテンツとの比較は、文字ごとの同等性ではなく、HTMLセマンティクスに基づいて行われます。 ほとんどの場合、空白は無視されます。属性の順序は重要ではありません。 詳細については、 assertHTMLEqual()を参照してください。
- SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix=, html=False)
Response
インスタンスが指定されたstatus_code
を生成し、text
が応答のコンテンツに表示されないことを表明します。html
をTrue
に設定して、text
をHTMLとして処理します。 応答コンテンツとの比較は、文字ごとの同等性ではなく、HTMLセマンティクスに基づいて行われます。 ほとんどの場合、空白は無視されます。属性の順序は重要ではありません。 詳細については、 assertHTMLEqual()を参照してください。
- SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix=, count=None)
指定された名前のテンプレートが応答のレンダリングに使用されたことを表明します。
名前は
'admin/index.html'
などの文字列です。count引数は、テンプレートをレンダリングする必要がある回数を示す整数です。 デフォルトは
None
です。これは、テンプレートを1回以上レンダリングする必要があることを意味します。これは、次のようにコンテキストマネージャーとして使用できます。
with self.assertTemplateUsed('index.html'): render_to_string('index.html') with self.assertTemplateUsed(template_name='index.html'): render_to_string('index.html')
- SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix=)
指定された名前のテンプレートが、応答のレンダリングに使用されていないことを表明します。
これは、 assertTemplateUsed()と同じ方法でコンテキストマネージャーとして使用できます。
- SimpleTestCase.assertURLEqual(url1, url2, msg_prefix=)
バージョン2.2の新機能。
同じ名前のパラメーターを除いて、クエリ文字列パラメーターの順序を無視して、2つのURLが同じであることを表明します。 たとえば、
/path/?x=1&y=2
は/path/?y=2&x=1
と同じですが、/path/?a=1&a=2
は/path/?a=2&a=1
と同じではありません。
- SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix=, fetch_redirect_response=True)
応答が
status_code
リダイレクトステータスを返し、expected_url
(GET
データを含む)にリダイレクトされ、最終ページがtarget_status_code
で受信されたことを表明します。リクエストで
follow
引数を使用した場合、expected_url
とtarget_status_code
がリダイレクトチェーンの最終ポイントのURLとステータスコードになります。fetch_redirect_response
がFalse
の場合、最終ページは読み込まれません。 テストクライアントは外部URLをフェッチできないため、これはexpected_url
がDjangoアプリの一部でない場合に特に役立ちます。2つのURLを比較する場合、スキームは正しく処理されます。 リダイレクト先の場所にスキームが指定されていない場合は、元のリクエストのスキームが使用されます。 存在する場合、
expected_url
のスキームは、との比較に使用されるスキームです。
- SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
文字列
html1
とhtml2
が等しいことを表明します。 比較はHTMLセマンティクスに基づいています。 比較では、次のことが考慮されます。HTMLタグの前後の空白は無視されます。
すべてのタイプの空白は同等と見なされます。
すべての開いているタグは暗黙的に閉じられます。 周囲のタグが閉じられたとき、またはHTMLドキュメントが終了したとき。
空のタグは、自動終了バージョンと同等です。
HTML要素の属性の順序は重要ではありません。
引数のない属性は、名前と値が等しい属性と同じです(例を参照)。
同じ文字を参照するテキスト、文字参照、およびエンティティ参照は同等です。
次の例は有効なテストであり、
AssertionError
を発生させません。self.assertHTMLEqual( '<p>Hello <b>'world'!</p>', '''<p> Hello <b>'world'! </b> </p>''' ) self.assertHTMLEqual( '<input type="checkbox" checked="checked" id="id_accept_terms" />', '<input id="id_accept_terms" type="checkbox" checked>' )
html1
およびhtml2
は有効なHTMLである必要があります。AssertionError
は、それらの1つを解析できない場合に発生します。エラー時の出力は、
msg
引数でカスタマイズできます。
- SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
文字列
html1
とhtml2
が等しくないことを表明します。 比較はHTMLセマンティクスに基づいています。 詳細については、 assertHTMLEqual()を参照してください。html1
およびhtml2
は有効なHTMLである必要があります。AssertionError
は、それらの1つを解析できない場合に発生します。エラー時の出力は、
msg
引数でカスタマイズできます。
- SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)
文字列
xml1
とxml2
が等しいことを表明します。 比較はXMLセマンティクスに基づいています。 assertHTMLEqual()と同様に、比較は解析されたコンテンツに対して行われるため、構文の違いではなく、セマンティックの違いのみが考慮されます。 無効なXMLがいずれかのパラメーターに渡されると、両方の文字列が同一であっても、AssertionError
が常に発生します。XML宣言、ドキュメントタイプ、およびコメントは無視されます。 ルート要素とその子のみが比較されます。
エラー時の出力は、
msg
引数でカスタマイズできます。
- SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)
文字列
xml1
とxml2
が等しくないことを表明します。 比較はXMLセマンティクスに基づいています。 詳細については、 assertXMLEqual()を参照してください。エラー時の出力は、
msg
引数でカスタマイズできます。
- SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix=)
HTMLフラグメント
needle
がhaystack
フラグメントに含まれていることを表明します。count
整数引数が指定されている場合、さらにneedle
オカレンスの数が厳密に検証されます。ほとんどの場合、空白は無視され、属性の順序は重要ではありません。 渡される引数は有効なHTMLである必要があります。
- SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None)
JSONフラグメント
raw
とexpected_data
が等しいことを表明します。 ヘビーウェイトがjson
ライブラリに委任されるため、通常のJSONの重要でない空白ルールが適用されます。エラー時の出力は、
msg
引数でカスタマイズできます。
- SimpleTestCase.assertJSONNotEqual(raw, expected_data, msg=None)
JSONフラグメント
raw
とexpected_data
が等しくないであることを表明します。 詳細については、 assertJSONEqual()を参照してください。エラー時の出力は、
msg
引数でカスタマイズできます。
- TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None)
クエリセット
qs
が特定の値のリストvalues
を返すことを表明します。qs
とvalues
の内容の比較は、transform
をqs
に適用することによって実行されます。 デフォルトでは、これはqs
の各値のrepr()
がvalues
と比較されることを意味します。repr()
が一意または有用な比較を提供しない場合は、他の呼び出し可能オブジェクトを使用できます。デフォルトでは、比較も順序に依存します。
qs
が暗黙的な順序付けを提供しない場合は、ordered
パラメーターをFalse
に設定できます。これにより、比較がcollections.Counter
比較に変わります。 順序が定義されていない場合(指定されたqs
が順序付けられておらず、比較が複数の順序付けられた値に対して行われる場合)、ValueError
が発生します。エラー時の出力は、
msg
引数でカスタマイズできます。
- TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)
func
が*args
および**kwargs
で呼び出されると、num
データベースクエリが実行されることを表明します。"using"
キーがkwargs
に存在する場合、クエリの数をチェックするためのデータベースエイリアスとして使用されます。self.assertNumQueries(7, using='non_default_db')
using
パラメーターを使用して関数を呼び出す場合は、呼び出しをlambda
でラップして、パラメーターを追加します。self.assertNumQueries(7, lambda: my_function(using=7))
これをコンテキストマネージャーとして使用することもできます。
with self.assertNumQueries(2): Person.objects.create(name="Aaron") Person.objects.create(name="Daniel")
タグ付けテスト
テストにタグを付けて、特定のサブセットを簡単に実行できるようにすることができます。 たとえば、高速テストまたは低速テストにラベルを付けることができます。
from django.test import tag
class SampleTestCase(TestCase):
@tag('fast')
def test_fast(self):
...
@tag('slow')
def test_slow(self):
...
@tag('slow', 'core')
def test_slow_but_core(self):
...
テストケースにタグを付けることもできます。
@tag('slow', 'core')
class SampleTestCase(TestCase):
...
サブクラスはスーパークラスからタグを継承し、メソッドはクラスからタグを継承します。 与えられた:
@tag('foo')
class SampleTestCaseChild(SampleTestCase):
@tag('bar')
def test(self):
...
SampleTestCaseChild.test
には、'slow'
、'core'
、'bar'
、および'foo'
のラベルが付けられます。
次に、実行するテストを選択できます。 たとえば、高速テストのみを実行するには、次のようにします。
または、高速テストとコアテストを実行するには(低速ですが):
タグでテストを除外することもできます。 遅くない場合にコアテストを実行するには:
test --exclude-tag
はtest --tag
よりも優先されるため、テストに2つのタグがあり、一方を選択してもう一方を除外すると、テストは実行されません。
メールサービス
Djangoビューのいずれかが Djangoのメール機能を使用してメールを送信する場合、そのビューを使用してテストを実行するたびにメールを送信したくない可能性があります。 このため、Djangoのテストランナーは、Djangoから送信されたすべての電子メールをダミーの送信トレイに自動的にリダイレクトします。 これにより、実際にメッセージを送信せずに、送信されたメッセージの数から各メッセージの内容まで、電子メールの送信のあらゆる側面をテストできます。
テストランナーは、通常の電子メールバックエンドをテストバックエンドに透過的に置き換えることでこれを実現します。 (心配しないでください。これは、マシンのメールサーバー(実行している場合)など、Django以外の他の電子メール送信者には影響しません。)
- django.core.mail.outbox
テスト実行中、各送信メールはdjango.core.mail.outbox
に保存されます。 これは、送信されたすべての EmailMessage インスタンスのリストです。 outbox
属性は、locmem
電子メールバックエンドが使用されている場合にのみで作成される特別な属性です。 通常、 django.core.mail モジュールの一部として存在することはなく、直接インポートすることはできません。 以下のコードは、この属性に正しくアクセスする方法を示しています。
django.core.mail.outbox
の長さと内容を調べるテストの例を次に示します。
from django.core import mail
from django.test import TestCase
class EmailTest(TestCase):
def test_send_email(self):
# Send message.
mail.send_mail(
'Subject here', 'Here is the message.',
'[email protected]', ['[email protected]'],
fail_silently=False,
)
# Test that one message has been sent.
self.assertEqual(len(mail.outbox), 1)
# Verify that the subject of the first message is correct.
self.assertEqual(mail.outbox[0].subject, 'Subject here')
以前で説明したように、Django *TestCase
では、すべてのテストの開始時にテスト送信ボックスが空になります。 送信トレイを手動で空にするには、空のリストをmail.outbox
に割り当てます。
from django.core import mail
# Empty the test outbox
mail.outbox = []
管理コマンド
管理コマンドは、 call_command()関数でテストできます。 出力はStringIO
インスタンスにリダイレクトできます。
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
class ClosepollTest(TestCase):
def test_command_output(self):
out = StringIO()
call_command('closepoll', stdout=out)
self.assertIn('Expected output', out.getvalue())
テストをスキップする
unittestライブラリは@skipIf
および@skipUnless
デコレータを提供し、特定の条件下でこれらのテストが失敗することが事前にわかっている場合にテストをスキップできるようにします。
たとえば、テストを成功させるために特定のオプションのライブラリが必要な場合は、テストケースを@skipIf
で装飾できます。 次に、テストランナーは、テストに失敗したり、テストを完全に省略したりする代わりに、テストが実行されなかったこととその理由を報告します。
これらのテストスキップ動作を補足するために、Djangoは2つの追加のスキップデコレータを提供します。 これらのデコレータは、一般的なブール値をテストする代わりに、データベースの機能をチェックし、データベースが特定の名前付き機能をサポートしていない場合はテストをスキップします。
デコレータは、文字列識別子を使用してデータベース機能を記述します。 この文字列は、データベース接続機能クラスの属性に対応します。 テストをスキップするための基礎として使用できるデータベース機能の完全なリストについては、django.db.backends.BaseDatabaseFeatures
クラスを参照してください。
- skipIfDBFeature(*feature_name_strings)
装飾されたテストをスキップするか、名前付きデータベース機能がすべてサポートされている場合はTestCase
をスキップします。
たとえば、データベースがトランザクションをサポートしている場合、次のテストは実行されません(たとえば、PostgreSQLでは実行されませんが、MyISAMテーブルを使用するMySQLでは実行されます)。
class MyTests(TestCase):
@skipIfDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass
- skipUnlessDBFeature(*feature_name_strings)
指定されたデータベース機能のいずれかがサポートされていない場合は、装飾されたテストをスキップするか、TestCase
をスキップしてください。
たとえば、次のテストは、データベースがトランザクションをサポートしている場合にのみ実行されます(たとえば、PostgreSQLでは実行されますが、MyISAMテーブルを使用するMySQLではではありません)。
class MyTests(TestCase):
@skipUnlessDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass