Django入门笔记【五】
入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/
*该笔记将使用一个关于投票网络应用(poll application)的例子来阐述Django的用法。
*自动测试(automated testing)
1. 首个测试
Question.was_published_recently()方法中有一个小问题,它会将未来的时间也显示为昨日发布。
使用shell来测试:
1 >>> import datetime 2 >>> from django.utils import timezone 3 >>> from polls.models import Question 4 >>> # create a Question instance with pub_date 30 days in the future 5 >>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30)) 6 >>> # was it published recently? 7 >>> future_question.was_published_recently() 8 True
2. 创建测试
测试系统会自动寻找任何以test开头的文件。在polls里创建tests.py:
1 # polls/tests.py 2 3 import datetime 4 5 from django.utils import timezone 6 from django.test import TestCase 7 8 from .models import Question 9 10 class QuestionMethodTests(TestCase): 11 12 def test_was_published_recently_with_future_question(self): 13 """ 14 was_published_recently should return False for questions whose 15 pub_date is in the future. 16 """ 17 time = timezone.now() + datetime.timedelta(days=30) 18 future_question = Question(pubdate=time) 19 self.assertEqual(future_question.was_published_recently(), False)
3. 运行测试
运行代码 $ python manage.py test polls
4. 订正错误
1 # polls/models.py 2 3 def was_published_recently(self): 4 now = timezone.now() 5 return now - datetime.timedelta(days=1) <= self.pub_date <= now
再次运行测试
5. 更复杂的测试
为了防止订正带来更多的错误,添加代码:
1 # polls/tests.py 2 3 def test_was_published_recently_with_old_question(self): 4 """ 5 was_published_recently() should return False for questions whose 6 pub_date is older than 1 day. 7 """ 8 time = timezone.now() - datetime.timedelta(days=30) 9 old_question = Question(pub_date=time) 10 self.assertEqual(old_question.was_published_recently(), False) 11 12 def test_was_published_recently_with_recent_question(self): 13 """ 14 was_published_recently() should return True for questions whose 15 pub_date is within the last day. 16 """ 17 time = timezone.now() - datetime.timedelta(hours=1) 18 recent_question = Question(pub_date=time) 19 self.assertEqual(recent_question.was_published_recently(), True)
6. 测试视图(Test a view)
Django提供了测试Client来模拟用户和视图的交互。我们可以在test.py或者shell中使用它。
先来看shell,初始化环境:
1 >>> from django.test.utils import setup_test_environment 2 >>> setup_test_environment()
再来导入Client:
1 >>> from django.test import Client 2 >>> # create an instance of the client for our use 3 >>> client = Client()
接下来:
1 >>> # get a response from '/' 2 >>> response = client.get('/') 3 >>> # we should expect a 404 from that address 4 >>> response.status_code 5 404 6 >>> # on the other hand we should expect to find something at '/polls/' 7 >>> # we'll use 'reverse()' rather than a hardcoded URL 8 >>> from django.core.urlresolvers import reverse 9 >>> response = client.get(reverse('polls: index')) 10 >>> response.status_code 11 200 12 >>> response.content 13 '\n\n\n <p>No polls are available.</p>\n\n' 14 >>> #note - you might get unexpected results of your ``TIME_ZONE`` 15 >>> # in ``settings.py`` is not correct. If you need to change it, 16 >>> # you will also need to restart your shell session 17 >>> from polls.models import Question 18 >>> from django.utils import timezone 19 >>> # create a Question and save it 20 >>> q = Question(question_text="Who is your favorite Beatle?", pub_date=timezone.now()) 21 >>> q.save() 22 >>> # check the response once again 23 >>> response = client.get('/polls/') 24 >>> response.content 25 '\n\n\n <ul>\n \n <li><a href="/polls/1/">Who is your favorite Beatle?</a> 26 </li>\n \n </ul>\n\n' 27 >>> # If the following doesn't work, you probably omitted the call to 28 >>> # setup_test_environment() described above 29 >>> response.context['latest_question_list'] 30 [<Question: Who is your favorite Beatle?>]
将视图
1 # polls/views.py 2 3 class IndexView(generic.ListView): 4 template_name = 'polls/index.html' 5 context_object_name = 'latest_question_list' 6 7 def get_queryset(self): 8 """Return the last five published questions.""" 9 return Question.objects.order_by('-pub_date')[:5]
改进为:
1 # polls/views.py 2 from django.utils import timezone 3 # 同时修改get_queryset方法 4 def get_queryset(self): 5 """ 6 Return the last five published questions (not including those set to 7 be published in the future). 8 """ 9 return Question.objects.filter( 10 pub_date_lte=timezone.now() 11 ).order_by('-pub_date')[:5]
接下来,基于之前在Shell里做的,我们来创建一个test文件:
1 # polls/tests.py 2 3 def create_question(question_text, days): 4 """ 5 Creates a question with the given `question_text` published the given 6 number of `days` offset to now (negative for questions published 7 in the past, positive for questions that have yet to be published). 8 """ 9 time = timezone.now() + datetime.timedelta(days=days) 10 return Question.objects.create(question_text=question_text, 11 pub_date=time) 12 13 14 class QuestionViewTests(TestCase): 15 def test_index_view_with_no_questions(self): 16 """ 17 If no questions exist, an appropriate message should be displayed. 18 """ 19 response = self.client.get(reverse('polls:index')) 20 self.assertEqual(response.status_code, 200) 21 self.assertContains(response, "No polls are available.") 22 self.assertQuerysetEqual(response.context['latest_question_list'], []) 23 24 def test_index_view_with_a_past_question(self): 25 """ 26 Questions with a pub_date in the past should be displayed on the 27 index page. 28 """ 29 create_question(question_text="Past question.", days=-30) 30 response = self.client.get(reverse('polls:index')) 31 self.assertQuerysetEqual( 32 response.context['latest_question_list'], 33 ['<Question: Past question.>'] 34 ) 35 36 def test_index_view_with_a_future_question(self): 37 """ 38 Questions with a pub_date in the future should not be displayed on 39 the index page. 40 """ 41 create_question(question_text="Future question.", days=30) 42 response = self.client.get(reverse('polls:index')) 43 self.assertContains(response, "No polls are available.", 44 status_code=200) 45 self.assertQuerysetEqual(response.context['latest_question_list'], []) 46 47 def test_index_view_with_future_question_and_past_question(self): 48 """ 49 Even if both past and future questions exist, only past questions 50 should be displayed. 51 """ 52 create_question(question_text="Past question.", days=-30) 53 create_question(question_text="Future question.", days=30) 54 response = self.client.get(reverse('polls:index')) 55 self.assertQuerysetEqual( 56 response.context['latest_question_list'], 57 ['<Question: Past question.>'] 58 ) 59 60 def test_index_view_with_two_past_questions(self): 61 """ 62 The questions index page may display multiple questions. 63 """ 64 create_question(question_text="Past question 1.", days=-30) 65 create_question(question_text="Past question 2.", days=-5) 66 response = self.client.get(reverse('polls:index')) 67 self.assertQuerysetEqual( 68 response.context['latest_question_list'], 69 ['<Question: Past question 2.>', '<Question: Past question 1.>'] 70 )
到目前为止,还不错。但是,如果用户仍然可以通过猜测,来进入未来的问题中,因此我们对DetailView也做相应改变:
1 # polls/views.py 2 3 class DetailView(generic.DetailView): 4 ... 5 def get_queryset(self): 6 """ 7 Excludes any questions that aren't published yet. 8 """ 9 return Question.objects.filter(pub_date__lte=timezone.now())
接下来,对此进行测试:
# polls/tests.py class QuestionIndexDetailTests(TestCase): def test_detail_view_with_a_future_question(self): """ The detail view of a question with a pub_date in the future should return a 404 not found. """ future_question = create_question(question_text='Future question.', days=5) response = self.client.get(reverse('polls:detail', args=(future_question.id,))) self.assertEqual(response.status_code, 404) def test_detail_view_with_a_past_question(self): """ The detail view of a question with a pub_date in the past should display the question's text. """ past_question = create_question(question_text='Past Question.', days=-5) response = self.client.get(reverse('polls:detail', args=(past_question.id,))) self.assertContains(response, past_question.question_text, status_code=200)
-- The End --