web-dev-qa-db-ja.com

Django=ビューの単体テストを書く方法は?

Django向けの単体テストの設計方法を理解するのに問題があります。

私の理解では、ビュー全体を一度にテストすることは不可能に思えます。リクエストの前後の状態を区別する必要があります。しかし、私はこれを設計する方法がわかりません。実生活の例はありますか?

ドキュメントを見ると、例は単純すぎてモデルにのみ焦点が当てられています。

@login_required
def call_view(request, contact_id):
    profile = request.user.get_profile()
    if request.POST:        
        form = CallsForm(profile.company, request.POST)           
        if form.is_valid()
        return HttpResponseRedirect('/contact/' + contact_id + '/calls/')
    else:        
        form = CallsForm(profile.company, instance=call)              
    variables = RequestContext(request, {'form':form}
    return render_to_response('conversation.html', variables)

更新:

成功テストを動作させようとしましたが、それでも失敗します:

def test_contact_view_success(self):
    # same again, but with valid data, then
    self.client.login(username='username1', password='password1')
    response = self.client.post('/contact/add/', {u'last_name': [u'Johnson'], }) 
    self.assertRedirects(response, '/')

エラーメッセージ:

AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)

これは、form.is_valid()が失敗し、リダイレクトしないためだと思いますか?

62
Houman

NB NB!以下で説明するのは、厳密には「単体テスト」ではありません。 Djangoコードを表示するための独立した単体テストを記述することはほとんど不可能です。これは統合テストの詳細です...

あなたの見解にはいくつかの経路があることは正しいです。

  1. GETまたはPOST匿名ユーザー(ログインページにリダイレクトする必要があります)
  2. GETまたはPOSTは、プロファイルのないログインユーザーによって(UserProfile.DoesNotExist_例外を発生させる必要があります)
  3. GETログインユーザーによる(フォームを表示する必要があります)
  4. POSTログインユーザーによる空白データ(フォームエラーが表示されます)
  5. 無効なデータを持つログインユーザーによるPOST(フォームエラーが表示されます)
  6. POST有効なデータを持つログインユーザーによる(リダイレクトする必要があります)

テスト1は実際には_@login_required_をテストしているだけなので、スキップできます。とにかくそれをテストする傾向があります(念のため、デコレーターを使用するのを忘れた場合に備えて)。

2の失敗ケース(500エラーページ)が本当に必要なものかわかりません。私はあなたが代わりに何をしたいのかを解決します(おそらく get_or_create() を使用するか、DoesNotExist例外をキャッチして新しいプロファイルをそのように作成します)。

カスタム検証の量によっては、4を実際にテストする必要がない場合があります。

いずれにしても、上記のすべてを考えると、私は次のようなことをします:

_from Django.test import TestCase

class TestCalls(TestCase):
    def test_call_view_denies_anonymous(self):
        response = self.client.get('/url/to/view', follow=True)
        self.assertRedirects(response, '/login/')
        response = self.client.post('/url/to/view', follow=True)
        self.assertRedirects(response, '/login/')

    def test_call_view_loads(self):
        self.client.login(username='user', password='test')  # defined in fixture or with factory in setUp()
        response = self.client.get('/url/to/view')
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'conversation.html')

    def test_call_view_fails_blank(self):
        self.client.login(username='user', password='test')
        response = self.client.post('/url/to/view', {}) # blank data dictionary
        self.assertFormError(response, 'form', 'some_field', 'This field is required.')
        # etc. ...

    def test_call_view_fails_invalid(self):
        # as above, but with invalid rather than blank data in dictionary

    def test_call_view_fails_invalid(self):
        # same again, but with valid data, then
        self.assertRedirects(response, '/contact/1/calls/')
_

明らかに、ここでの欠点はハードコードされたURLです。テストで se reverse() を使用するか、 RequestFactoryを使用してリクエストを作成します を使用して、ビューをメソッドとして(URLではなく)呼び出すことができます。ただし、後者の方法では、リダイレクト対象をテストするためにハードコードされた値またはreverse()を使用する必要があります。

お役に立てれば。

101
supervacuo

Djangoには、完全な要求/応答サイクルをテストするために使用できるテストクライアントが付属しています: docs には、特定のurlおよびステータスコードとテンプレートコンテキストのアサート。また、POSTを実行し、期待どおりに成功したリダイレクトをアサートするテストも必要です。

5
Mark Lavin