Djangoのチュートリアル中にオリジナルのテストを追加した話
はじめに
オリジナルと言っても、
はじめての Django アプリ作成、その 5 | Django ドキュメント | Django
ここにある「さらなるテストについて考える」で述べられているChoicesを持たないQuestionsが公開されないようテストするだけのものですが。
この記事ではコードの一部を記述します。
実装
- Model
class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text
このモデルではQuestionとChoiceが一対多の関係となっています。
ちなみにDjangoでは多対一の関係にはForeignKeyを、多対多の関係にはManyToManyFieldを、一対一の関係にはOneToOneFieldを使います。
- View
class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list' def get_queryset(self): """ Return the last five published questions with any choices.(not including those set to be published in the future). """ question = Question.objects.filter( pub_date__lte=timezone.now(), choice__isnull=False ) return question.order_by('-pub_date')[:5]
このクラス内のget_queryset()で最近の5個のQuestionsを返しています。filterメソッドで現在以前の、Choiceを持っているQuestionという条件で検索し一致するものを返します。
- Test
def create_question(question_text, days): """ Create a question with the given 'question_text' and published the given number of 'days' offset to now(negative for questions published in the past, positive for questions that have yet to be published). """ time = timezone.now() + datetime.timedelta(days=days) return Question.objects.create(question_text=question_text, pub_date=time) def create_choice(question, choice_text): """ Create a choice of the given 'question' object. """ question.choice_set.create(choice_text=choice_text, votes=0) class QuestionIndexViewTests(TestCase): ... def test_past_question_without_choices(self): """ The questions without choices aren't displayed on the index page. """ question = create_question(question_text="Without choices.", days=-30) question_with_choice = create_question(question_text="With choice.", days=-30) create_choice(question_with_choice, choice_text="Choice 1") response = self.client.get(reverse('polls:index')) self.assertQuerysetEqual( response.context['latest_question_list'], ['<Question: With choice.>'] )
test_past_question_without_choices()でChoiceありとなしのQuestionを作成し、ありのものだけ表示されるようテストしています。
結果
- Viewにて、choice__isnull=Falseを書かなかった場合
> python manage.py test polls Creating test database for alias 'default'... System check identified no issues (0 silenced). ......F.... ====================================================================== FAIL: test_past_question_without_choices (polls.tests.QuestionIndexViewTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/*****/Django-Project/mysite/polls/tests.py", line 122, in test_past_question_without_choices ['<Question: With choice.>'] File "/Users/*****/.virtualenvs/django/lib/python3.6/site-packages/django/test/testcases.py", line 955, in assertQuerysetEqual return self.assertEqual(list(items), values, msg=msg) AssertionError: Lists differ: ['<Question: With choice.>', '<Question: Without choices.>'] != ['<Question: With choice.>'] First list contains 1 additional elements. First extra element 1: '<Question: Without choices.>' - ['<Question: With choice.>', '<Question: Without choices.>'] + ['<Question: With choice.>'] ---------------------------------------------------------------------- Ran 11 tests in 0.060s FAILED (failures=1) Destroying test database for alias 'default'...
- 書いた場合
> python manage.py test polls Creating test database for alias 'default'... System check identified no issues (0 silenced). ........... ---------------------------------------------------------------------- Ran 11 tests in 0.054s OK Destroying test database for alias 'default'...
以上のように、テストが通らなかった場合どのテストで、どのように通らなかったのか教えてくれます。