【Django】はじめてのDjangoアプリ第10回

django

PythonのWEBフレームワークDjangoを利用したアプリケーションの公式テュートリアルに沿ったWEBアプリケーション作成のメモ第10回です。

Writing your first Django app, part 5 | Django documentation
The web framework for perfectionists with deadlines.

前回は汎用ビューの適用をしました。

今回は、自動テストを実施していきます。

バグの特定

幸いなことに、polls アプリケーションにはすぐに修正できる小さなバグがあります。

Question.was_published_recently() メソッドは、Question が過去 1 日以内に公開された場合 (これは正しいです)にTrue を返しますが、Questionpub_date フィールドが未来日付の場合にもTrue を返します(これは正しくありません)。

shell を使用して、日付が未来の質問のメソッドをチェックして、バグを確認します。

python manage.py shell
import datetime
from django.utils import timezone
from polls.models import Question
# pub_dateが30日後のQuestionインスタンスを作成
future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
# 最近公開されたか判定
future_question.was_published_recently()

未来のことは「最近」ではないので、これは明らかに間違っています。

バグを明らかにするテストを作成

問題をテストするために shell で行ったことは、まさに自動テストで実行できることなので、これを自動テストに変えてみましょう。

アプリケーションのテストの従来の場所は、アプリケーションの tests.py ファイル内です。テスト システムは、名前が test で始まるファイル内のテストを自動的に検索します。

polls アプリケーションの tests.py ファイルに次の内容を記述します。

▼「polls/tests.py」を編集

import datetime

from django.test import TestCase
from django.utils import timezone

from .models import Question


class QuestionModelTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() returns False for questions whose pub_date
        is in the future.
        """
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)

ここでは、未来の pub_date を持つ Question インスタンスを作成するメソッドを含む django.test.TestCase サブクラスを作成しました。

そのあとで、was_published_recently() の出力を確認します。

これは False であるべきです。

テスト実行

ターミナルでテストを実行できます。

python manage.py test polls

すると次のようなものが表示されます:

何が起こったのかは次のとおりです。

🟢manage.py test pollspolls アプリケーションでテストを探しました
🟢django.test.TestCase クラスのサブクラスが見つかりました
🟢テスト用の特別なデータベースを作成しました
🟢名前が test で始まる テストメソッド を探しました。
🟢test_was_published_recently_with_future_question では、pub_date フィールドが 30 日後の Question インスタンスを作成しました。
🟢…そして、assertIs() メソッドを使用して、was_published_recently() には False を返してほしかったのですが True を返すことがわかりました。

テストでは、どのテストが失敗したか、さらに失敗が発生した行も通知されます。

バグ修正

問題が何であるかはすでにわかっています。pub_date が未来の場合、Question.was_published_recently()False を返すはずです。日付が過去の場合にのみ True を返すように、models.py のメソッドを修正します。

▼「polls/models.py」を編集

※「was_published_recently()」を置換

def was_published_recently(self):
    now = timezone.now()
    return now - datetime.timedelta(days=1) <= self.pub_date <= now

そしてテストを再度実行します。

バグを特定した後、それを明らかにするテストを作成し、コード内のバグを修正してテストが合格するようにしました。

将来的には、他にも多くのことがアプリケーションで問題を起こす可能性がありますが、テストを実行するとすぐに警告が表示されるため、不注意でこのバグが再び持ち込まれることはないと確信できます。アプリケーションのこの小さな部分は、永久に安全に固定されると考えることができます。

より包括的なテスト

ここで、was_published_recently() メソッドをさらに特定することができます。実際、あるバグを修正する際に別のバグを導入してしまったら、間違いなく恥ずかしいことになるでしょう。

さらに 2 つのテスト メソッドを同じクラスに追加して、メソッドの動作をより包括的にテストします。

▼「polls/tests.py」を編集

※以下のコードを末尾に追記

    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() returns False for questions whose pub_date
        is older than 1 day.
        """
        time = timezone.now() - datetime.timedelta(days=1, seconds=1)
        old_question = Question(pub_date=time)
        self.assertIs(old_question.was_published_recently(), False)

    def test_was_published_recently_with_recent_question(self):
        """
        was_published_recently() returns True for questions whose pub_date
        is within the last day.
        """
        time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)

そして、Question.was_published_recently() が過去、最近、将来の質問に対して適切な値を返すことを確認する 3 つのテストができました。

繰り返しますが、polls は最小限のアプリケーションですが、将来的にどれほど複雑になり、他のどのコードと対話しても、テストを作成したメソッドが期待どおりに動作するというある程度の保証が得られます。

今回は以上です。次回はビューのテストを予定しています。

  • 0
  • 0
  • 0
  • 0

コメント

タイトルとURLをコピーしました