diff --git a/first_django_app.md b/first_django_app.md index 74d7a0d..576fb42 100644 --- a/first_django_app.md +++ b/first_django_app.md @@ -528,4 +528,140 @@ Write more tests to make sure it's comprehensive 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) -``` \ No newline at end of file +``` + +### 5.2 Test a view + +Target: 如果 pub_date 设置为未来某天,这应该被解释为这个问题将在所填写的时间点才被发布,而在之前是不可见的。 + +#### A test for a view + +We use TDD (i.e. test first, then develop) + +#### The Django test client + +Django provides a test **Client** (a class) to simulate a user interacting with the code at the view level. + +We can try it in interactive shell following the guide + +#### Improving our view + +Add following in `tests.py` + +```python +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) + + +class QuestionIndexViewTests(TestCase): + def test_no_questions(self): + """ + If no questions exist, an appropriate message is displayed. + """ + response = self.client.get(reverse('polls:index')) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "No polls are available.") + self.assertQuerysetEqual(response.context['latest_question_list'], []) + + def test_past_question(self): + """ + Questions with a pub_date in the past are displayed on the + index page. + """ + create_question(question_text="Past question.", days=-30) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + [''] + ) + + def test_future_question(self): + """ + Questions with a pub_date in the future aren't displayed on + the index page. + """ + create_question(question_text="Future question.", days=30) + response = self.client.get(reverse('polls:index')) + self.assertContains(response, "No polls are available.") + self.assertQuerysetEqual(response.context['latest_question_list'], []) + + def test_future_question_and_past_question(self): + """ + Even if both past and future questions exist, only past questions + are displayed. + """ + create_question(question_text="Past question.", days=-30) + create_question(question_text="Future question.", days=30) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + [''] + ) + + def test_two_past_questions(self): + """ + The questions index page may display multiple questions. + """ + create_question(question_text="Past question 1.", days=-30) + create_question(question_text="Past question 2.", days=-5) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + ['', ''] + ) +``` + +Testing generated + +``` +Creating test database for alias 'default'... +System check identified no issues (0 silenced). +FF...... +====================================================================== +FAIL: test_future_question (polls.tests.QuestionIndexViewTests) +Questions with a pub_date in the future aren't displayed on +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/home/jason/HomeWorkstation/SynologyGiteaSpace/djangoproject/src/polls/tests.py", line 77, in test_future_question + self.assertContains(response, "No polls are available.") + File "/home/jason/miniconda3/envs/django/lib/python3.8/site-packages/django/test/testcases.py", line 470, in assertContains + self.assertTrue(real_count != 0, msg_prefix + "Couldn't find %s in response" % text_repr) +AssertionError: False is not true : Couldn't find 'No polls are available.' in response + +====================================================================== +FAIL: test_future_question_and_past_question (polls.tests.QuestionIndexViewTests) +Even if both past and future questions exist, only past questions +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/home/jason/HomeWorkstation/SynologyGiteaSpace/djangoproject/src/polls/tests.py", line 88, in test_future_question_and_past_question + self.assertQuerysetEqual( + File "/home/jason/miniconda3/envs/django/lib/python3.8/site-packages/django/test/testcases.py", line 1052, in assertQuerysetEqual + return self.assertEqual(list(items), values, msg=msg) +AssertionError: Lists differ: ['', ''] != [''] + +First differing element 0: +'' +'' + +First list contains 1 additional elements. +First extra element 1: +'' + +- ['', ''] ++ [''] + +---------------------------------------------------------------------- +Ran 8 tests in 0.018s + +FAILED (failures=2) +Destroying test database for alias 'default'... +``` + +Fix it by change `get_queryset()` method in Question. 让他它能通过将 Question 的 pub_data 属性与 timezone.now() 相比较来判断是否应该显示此 Question + diff --git a/src/polls/tests.py b/src/polls/tests.py index 3a221a0..7062709 100644 --- a/src/polls/tests.py +++ b/src/polls/tests.py @@ -2,6 +2,7 @@ import datetime from django.test import TestCase from django.utils import timezone +from django.urls import reverse from .models import Question @@ -32,4 +33,71 @@ class QuestionModelTests(TestCase): 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) \ No newline at end of file + self.assertIs(recent_question.was_published_recently(), True) + +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) + + +class QuestionIndexViewTests(TestCase): + def test_no_questions(self): + """ + If no questions exist, an appropriate message is displayed. + """ + response = self.client.get(reverse('polls:index')) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "No polls are available.") + self.assertQuerysetEqual(response.context['latest_question_list'], []) + + def test_past_question(self): + """ + Questions with a pub_date in the past are displayed on the + index page. + """ + create_question(question_text="Past question.", days=-30) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + [''] + ) + + def test_future_question(self): + """ + Questions with a pub_date in the future aren't displayed on + the index page. + """ + create_question(question_text="Future question.", days=30) + response = self.client.get(reverse('polls:index')) + self.assertContains(response, "No polls are available.") + self.assertQuerysetEqual(response.context['latest_question_list'], []) + + def test_future_question_and_past_question(self): + """ + Even if both past and future questions exist, only past questions + are displayed. + """ + create_question(question_text="Past question.", days=-30) + create_question(question_text="Future question.", days=30) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + [''] + ) + + def test_two_past_questions(self): + """ + The questions index page may display multiple questions. + """ + create_question(question_text="Past question 1.", days=-30) + create_question(question_text="Past question 2.", days=-5) + response = self.client.get(reverse('polls:index')) + self.assertQuerysetEqual( + response.context['latest_question_list'], + ['', ''] + ) \ No newline at end of file diff --git a/src/polls/views.py b/src/polls/views.py index 9e86277..7d6e42c 100644 --- a/src/polls/views.py +++ b/src/polls/views.py @@ -3,6 +3,7 @@ from django.shortcuts import render, get_object_or_404 from django.template import loader from django.urls import reverse from django.views import generic +from django.utils import timezone from .models import Question, Choice @@ -11,8 +12,13 @@ class IndexView(generic.ListView): context_object_name = 'latest_question_list' def get_queryset(self): - """Return the last five published questions.""" - return Question.objects.order_by('-pub_date')[:5] + """ + Return the last five published questions (not including those set to be published in the future). + """ + return Question.objects.filter( + pub_date__lte=timezone.now() + ).order_by('-pub_date')[:5] + # return Question.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Question