サイドバーの「Django ドキュメント検索」というプレースホルダの入った検索では、Django ドキュメント (Django 2.1) の公式サイト内を検索できます
Django チュートリアルの進め方 その 5 (1)

目次

はじめに

Django を学習するには、何はともあれ公式チュートリアル「はじめての Django アプリ作成」からスタートしてみるのが良いでしょう。しかし、いざチュートリアル通りに進めようとしても躓く場合も多いと思います。そこで、「チュートリアルの進め方」シリーズとして、チュートリアルに沿った作業を行いながら補足事項をまとめることにしました。チュートリアルで躓いた際のヒントとしてお使いください。

利用環境は Ubuntu、 Linux Mint を中心として説明しますが、 Windows についても補足します。

前回は、Django チュートリアルの進め方 その 4 (2) と題してはじめての Django アプリ作成、その 4の後半についてまとめました。

今回は、はじめての Django アプリ作成、その 5 の前半についてまとめました。

初めてのテスト作成

バグを見つけたとき

公開日が未来の日付であった場合に、was_published_recently() がどのような動作をするか shell で確認します。

$ python manage.py shell
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> future_question.was_published_recently()
True


5行目で、30日後の公開日の Question を作成しています。

was_published_recently() は「最近公開された質問か?」を判断するメソッドですが、30日後に公開される (まだ公開されていない) 予定の質問に対して True を返してしまっています。つまりバグです。

バグをあぶり出すためにテストを作成する

shell でテストした内容をテストコードにします。
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)

TestCase を継承したクラスに、test で始まるメソッドを追加。メソッド中で評価用のメソッド (ここでは assertIs()) を実行。という手順です。
django.test.TestCase は Python の標準のユニットテストクラスである TestCase を拡張したものです。基本的な使い方は unittest.TestCase のリファレンスを参照してください。下記は、主な検査用メソッドの抜粋です。

メソッド 確認事項
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)

テストの実行

テストを実行します。(manage.py test アプリケーション名)

$ python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jade/workspace/mysite/polls/tests.py", line 18, in test_was_published_recently_with_future_question
    self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)
Destroying test database for alias 'default'...

バグを修正する

was_published_recently() が未来日付で True を返さないように修正します。

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

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

$ python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database for alias 'default'...

より包括的なテスト

テストを完成させます。

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)

    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)

公開日が前日より前、前日以降、未来の3パターンでテストするようにしています。

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

$ python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
Destroying test database for alias 'default'...

3つのテストすべて合格した事がわかります。

今回はテストの基本についてまとめました。次回は はじめての Django アプリ作成、その 5 の後半、ビューのテストについてまとめます。

参考

低価格なのに高速・多機能・高セキュリティ 月額400円(税別)から最大容量100GB WordPress専用高速サーバー Z.com WP
おすすめの記事