テストツール
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)
構築時に引数は必要ありません。 ただし、キーワード引数を使用して、いくつかのデフォルトヘッダーを指定できます。 たとえば、これにより、各リクエストで
User-AgentHTTPヘッダーが送信されます。>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')get()、 post()などに渡される
extraキーワード引数の値。 クラスコンストラクタに渡されるデフォルトよりも優先されます。enforce_csrf_checks引数は、CSRF保護をテストするために使用できます(上記を参照)。json_encoder引数を使用すると、 post()で説明されているJSONシリアル化用のカスタムJSONエンコーダーを設定できます。raise_request_exception引数を使用すると、要求中に発生した例外をテストでも発生させるかどうかを制御できます。 デフォルトはTrueです。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=7extraキーワードargumentsパラメーターを使用して、リクエストで送信されるヘッダーを指定できます。 例えば:>>> c = Client() >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, ... HTTP_ACCEPT='application/json')…HTTPヘッダー
HTTP_ACCEPTを詳細ビューに送信します。これは、 django.http.HttpRequest.accepts()メソッドを使用するコードパスをテストするための良い方法です。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=secretcontent_typeを application / json として指定した場合、dataは、dict、リスト、またはタプルの場合、json.dumps()を使用してシリアル化されます。 シリアル化はデフォルトで DjangoJSONEncoder を使用して実行され、 Client にjson_encoder引数を指定することでオーバーライドできます。 このシリアル化は、 put()、 patch()、および delete()要求でも発生します。他の
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', 'rb') 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つの値のタプル。
値は(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 例外が発生します。
通常の応答と同様に、 HttpResponse.headers を介してヘッダーにアクセスすることもできます。 たとえば、response.headers['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=Djangoユニットテストクラスの階層|Djangoユニットテストクラスの階層]]
通常のunittest.TestCaseを任意のサブクラスに変換できます。テストの基本クラスをunittest.TestCaseからサブクラスに変更します。 すべての標準Pythonユニットテスト機能が利用可能になり、以下の各セクションで説明するように、いくつかの便利な追加機能が追加されます。
SimpleTestCase
- class SimpleTestCase
この機能を追加するunittest.TestCaseのサブクラス:
- 次のようないくつかの有用なアサーション:
- 呼び出し可能が特定の例外を発生させることを確認します。
- 呼び出し可能が特定の警告をトリガーすることを確認します。
- フォームフィールドのテストレンダリングとエラー処理。
- 特定のフラグメントの有無について HTML応答をテストします。
- テンプレートが特定の応答コンテンツを生成するために使用されている/使用されていないことを確認します。
- 2つの URL が等しいことを確認します。
- HTTP リダイレクトの確認はアプリによって実行されます。
- 2つの HTMLフラグメントの同等性/不同等性または封じ込めを堅牢にテストします。
- 2つの XMLフラグメントの同等性/不同等性を堅牢にテストします。
- 2つの JSONフラグメントが等しいかどうかを堅牢にテストします。
- 変更された設定でテストを実行する機能。
- クライアント クライアントを使用します。
テストでデータベースクエリを実行する場合は、サブクラス TransactionTestCase または TestCase を使用します。
- SimpleTestCase.databases
- SimpleTestCase は、デフォルトでデータベースクエリを許可しません。 これは、各
SimpleTestCaseテストがトランザクションで実行されないため、他のテストに影響を与える書き込みクエリの実行を回避するのに役立ちます。 この問題を気にしない場合は、テストクラスで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()を呼び出す前に必ず例外を発生させてください。
バージョン3.1で変更: debug()メソッドが実装され、結果を収集して例外をキャッチせずにテストを実行できるようになりました。
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()が呼び出され、速度の利点が無効になることに注意してください。バージョン3.2での変更:
setUpTestData()のクラス属性に割り当てられたオブジェクトは、各テストメソッドによって実行される変更からそれらを分離するために、copy.deepcopy()でのディープコピーの作成をサポートする必要があります。 Djangoの以前のバージョンでは、これらのオブジェクトは再利用され、それらに加えられた変更はテストメソッド間で保持されていました。
- classmethod TestCase.captureOnCommitCallbacks(using=DEFAULT_DB_ALIAS, execute=False)
バージョン3.2の新機能。
指定されたデータベース接続の transaction.on_commit()コールバックをキャプチャするコンテキストマネージャーを返します。 コンテキストの終了時に、キャプチャされたコールバック関数を含むリストを返します。 このリストから、コールバックに対してアサーションを作成するか、コールバックを呼び出して副作用を呼び出し、コミットをエミュレートできます。
usingは、コールバックをキャプチャするデータベース接続のエイリアスです。executeがTrueの場合、例外が発生していなければ、すべてのコールバックはコンテキストマネージャーの終了時に呼び出されます。 これは、ラップされたコードブロックの後のコミットをエミュレートします。例えば:
from django.core import mail from django.test import TestCase class ContactTests(TestCase): def test_post(self): with self.captureOnCommitCallbacks(execute=True) as callbacks: response = self.client.post( '/contact/', {'message': 'I like your site'}, ) self.assertEqual(response.status_code, 200) self.assertEqual(len(callbacks), 1) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Contact Form') self.assertEqual(mail.outbox[0].body, 'I like your site')
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
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にないデータベースに対するクエリでは、テスト間の状態リークを防ぐためにアサーションエラーが発生します。
- TestCase.databases
デフォルトでは、defaultデータベースのみがTestCaseの実行中にトランザクションにラップされ、他のデータベースにクエリを実行しようとすると、テスト間の状態リークを防ぐためにアサーションエラーが発生します。
テストクラスのdatabasesクラス属性を使用して、default以外のデータベースに対するトランザクションラッピングを要求します。
例えば:
class OtherDBTests(TestCase):
databases = {'other'}
def test_other_db_query(self):
...
このテストでは、otherデータベースに対するクエリのみが許可されます。 SimpleTestCase.databases および TransactionTestCase.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つの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オカレンスの数が厳密に検証されます。ほとんどの場合、空白は無視され、属性の順序は重要ではありません。 詳細については、 assertHTMLEqual()を参照してください。
- 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=None, ordered=True, msg=None)
クエリセット
qsが特定の反復可能な値valuesに一致することを表明します。transformが指定されている場合、valuesは、qsの各メンバーにtransformを適用して作成されたリストと比較されます。デフォルトでは、比較も順序に依存します。
qsが暗黙的な順序付けを提供しない場合は、orderedパラメーターをFalseに設定できます。これにより、比較がcollections.Counter比較に変わります。 順序が定義されていない場合(指定されたqsが順序付けられておらず、比較が複数の順序付けられた値に対して行われる場合)、ValueErrorが発生します。エラー時の出力は、
msg引数でカスタマイズできます。バージョン3.2で変更:
transform引数のデフォルト値がNoneに変更されました。バージョン3.2の新機能:クエリセット間の直接比較のサポートが追加されました。
バージョン3.2以降非推奨:
transformが提供されておらず、valuesが文字列のリストである場合、それぞれにrepr()を適用して作成されたリストと比較されます。qsのメンバー。 この動作は非推奨であり、Django4.1で削除される予定です。 必要に応じて、transformをreprに明示的に設定してください。
- 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つのタグがあり、一方を選択してもう一方を除外すると、テストは実行されません。
非同期コードのテスト
バージョン3.1の新機能。
非同期ビューの出力をテストするだけの場合、標準のテストクライアントは、追加の作業を必要とせずに、独自の非同期ループ内でビューを実行します。
ただし、Djangoプロジェクトの完全非同期テストを作成する場合は、いくつかの点を考慮する必要があります。
まず、テストはテストクラスのasync defメソッドである必要があります(非同期コンテキストを提供するため)。 Djangoは、async defテストを自動的に検出し、それらをラップして、独自のイベントループで実行されるようにします。
非同期関数からテストする場合は、非同期テストクライアントも使用する必要があります。 これは、django.test.AsyncClientとして、または任意のテストでself.async_clientとして利用できます。
AsyncClientには、2つの例外を除いて、同期(通常)テストクライアントと同じメソッドとシグネチャがあります。
followパラメーターはサポートされていません。extraキーワード引数として渡されるヘッダーには、同期クライアントに必要なHTTP_プレフィックスを付けないでください( Client.get()を参照)。 たとえば、HTTPAcceptヘッダーを設定する方法は次のとおりです。>>> c = AsyncClient() >>> c.get( ... '/customers/details/', ... {'name': 'fred', 'age': 7}, ... ACCEPT='application/json' ... )
AsyncClientを使用して、要求を行うすべてのメソッドを待機する必要があります。
async def test_my_thing(self):
response = await self.async_client.get('/some-url/')
self.assertEqual(response.status_code, 200)
非同期クライアントは同期ビューを呼び出すこともできます。 両方をサポートするDjangoの非同期リクエストパスを介して実行されます。 AsyncClientを介して呼び出されたビューは、通常のクライアントが作成するWSGIRequestではなく、requestのASGIRequestオブジェクトを取得します。
警告
テストデコレータを使用している場合、正しく機能するようにするには、非同期互換である必要があります。 Djangoの組み込みデコレータは正しく動作しますが、サードパーティのデコレータは実行されないように見える場合があります(テストではなく実行フローの間違った部分を「ラップ」します)。
これらのデコレータを使用する必要がある場合は、代わりに async_to_sync() inside でテストメソッドをデコレートする必要があります。
from asgiref.sync import async_to_sync
from django.test import TestCase
class MyTests(TestCase):
@mock.patch(...)
@async_to_sync
async def test_my_thing(self):
...
メールサービス
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つの追加のスキップデコレータを提供します。 これらのデコレータは、一般的なブール値をテストする代わりに、データベースの機能をチェックし、データベースが特定の名前付き機能をサポートしていない場合はテストをスキップします。
デコレータは、文字列識別子を使用してデータベース機能を記述します。 この文字列は、データベース接続機能クラスの属性に対応します。 見る :source: `django.db.backends.base.features.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