Django 学习 Part5、6 :自动化测试与静态文件管理
一、为什么要写测试测试是检查代码操作的例程在不同级别上运行单元测试检查微小细节如模型方法返回值集成测试检查整体操作如用户输入序列是否产生预期结果自动化测试的优势一次创建重复运行修改代码后自动检查是否破坏原有功能无需耗时的人工测试测试哲学你不需要测试整个代码库可以逐步添加测试一个测试总比没有测试好二、发现 Bugwas_published_recently() 的问题我们的Question模型有一个was_published_recently()方法要求如果问题在一天内发布则返回True。但它有个 Bug当pub_date是未来时间时也会返回True在 shell 中验证importdatetimefromdjango.utilsimporttimezonefrompolls.modelsimportQuestion# 创建30天后的未来问题future_questionQuestion(pub_datetimezone.now()datetime.timedelta(days30))future_question.was_published_recently()True# 错误未来时间不应该算最近三、编写第一个测试3.1 创建测试文件在polls/tests.py中编写测试importdatetimefromdjango.testimportTestCasefromdjango.utilsimporttimezonefrom.modelsimportQuestionclassQuestionModelTests(TestCase):deftest_was_published_recently_with_future_question(self): was_published_recently() 对于发布日期在将来的问题应返回 False timetimezone.now()datetime.timedelta(days30)future_questionQuestion(pub_datetime)self.assertIs(future_question.was_published_recently(),False)3.2 运行测试$ python manage.pytestpolls测试执行流程寻找polls应用中的测试代码找到django.test.TestCase的子类创建特殊测试数据库寻找以test开头的方法创建pub_date为30天后的Question实例使用assertIs()检查返回值预期结果测试失败F因为我们尚未修复 Bug四、修复 Bug 并全面测试4.1 修复模型方法修改polls/models.pydefwas_published_recently(self):nowtimezone.now()returnnow-datetime.timedelta(days1)self.pub_datenow4.2 添加全面测试classQuestionModelTests(TestCase):deftest_was_published_recently_with_future_question(self):将来日期返回 Falsetimetimezone.now()datetime.timedelta(days30)future_questionQuestion(pub_datetime)self.assertIs(future_question.was_published_recently(),False)deftest_was_published_recently_with_old_question(self):超过一天的过去日期返回 Falsetimetimezone.now()-datetime.timedelta(days1,seconds1)old_questionQuestion(pub_datetime)self.assertIs(old_question.was_published_recently(),False)deftest_was_published_recently_with_recent_question(self):一天内的日期返回 Truetimetimezone.now()-datetime.timedelta(hours23,minutes59,seconds59)recent_questionQuestion(pub_datetime)self.assertIs(recent_question.was_published_recently(),True)五、测试视图5.1 创建测试工具函数在polls/tests.py中添加defcreate_question(question_text,days): 创建问题的工具函数 days: 负数表示过去正数表示将来 timetimezone.now()datetime.timedelta(daysdays)returnQuestion.objects.create(question_textquestion_text,pub_datetime)5.2 测试 IndexViewfromdjango.urlsimportreverseclassQuestionIndexViewTests(TestCase):deftest_no_questions(self):没有问题时显示适当消息responseself.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],[])deftest_past_question(self):过去的问题显示在首页past_questioncreate_question(question_textPast question.,days-30)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[past_question],)deftest_future_question(self):将来的问题不显示在首页create_question(question_textFuture question.,days30)responseself.client.get(reverse(polls:index))self.assertContains(response,No polls are available.)self.assertQuerySetEqual(response.context[latest_question_list],[])deftest_future_question_and_past_question(self):同时存在过去和将来的问题时只显示过去的past_questioncreate_question(question_textPast question.,days-30)create_question(question_textFuture question.,days30)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[past_question],)deftest_two_past_questions(self):显示多个过去的问题question1create_question(question_textPast question 1.,days-30)question2create_question(question_textPast question 2.,days-5)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[question2,question1],)5.3 测试 DetailView首先我们需要限制详情页不显示未来的问题。修改polls/views.pyclassDetailView(generic.DetailView):modelQuestion template_namepolls/detail.htmldefget_queryset(self):排除尚未发布的问题returnQuestion.objects.filter(pub_date__ltetimezone.now())然后添加测试classQuestionDetailViewTests(TestCase):deftest_future_question(self):未来问题的详情页返回 404future_questioncreate_question(question_textFuture question.,days5)urlreverse(polls:detail,args(future_question.id,))responseself.client.get(url)self.assertEqual(response.status_code,404)deftest_past_question(self):过去问题的详情页显示问题内容past_questioncreate_question(question_textPast Question.,days-5)urlreverse(polls:detail,args(past_question.id,))responseself.client.get(url)self.assertContains(response,past_question.question_text)第六部分静态文件与样式定制一、静态文件基础1.1 什么是静态文件静态文件是 Web 应用直接返回的内容片段CSS 样式表JavaScript 文件图片文件第三方前端框架1.2 创建静态文件目录在polls目录下创建以下结构polls/ static/ polls/ style.css images/ background.gif命名空间的重要性虽然可以直接放在polls/static/但最佳实践是再创建一层与应用同名的子目录polls/static/polls/。这避免了不同应用中同名静态文件的冲突——Django 只会使用第一个找到的静态文件二、自定义应用样式2.1 创建 CSS 文件创建polls/static/polls/style.css/* 链接样式绿色 */li a{color:green;}/* 页面背景图 */body{background:whiteurl(images/background.gif)no-repeat;}2.2 在模板中引用静态文件修改polls/templates/polls/index.html在文件头部添加{% load static %}linkrelstylesheettypetext/csshref{% static polls/style.css %}关键说明{% load static %}加载静态文件模板标签{% static polls/style.css %}生成静态文件的绝对路径这种方式允许你更改STATIC_URL而无需修改大量静态文件2.3 添加背景图片在polls/static/polls/下创建images目录放入名为background.gif的图片CSS 中已配置body背景图路径使用相对路径images/background.gif警告{% static %}标签在静态文件如 CSS中不可用因为它们不是由 Django 生成的。始终使用相对路径在静态文件之间相互引用三、静态文件配置详解3.1 开发环境配置确保settings.py中包含INSTALLED_APPS[django.contrib.staticfiles,# 确保已启用# ...]STATIC_URL/static/# 访问静态文件的 URL 前缀# 开发时额外查找位置可选STATICFILES_DIRS[BASE_DIR/static,]3.2 查找器机制Django 的STATICFILES_FINDERS设置包含一系列查找器AppDirectoriesFinder在每个INSTALLED_APPS的子目录static中查找四、完整代码汇总polls/tests.py完整版importdatetimefromdjango.testimportTestCasefromdjango.urlsimportreversefromdjango.utilsimporttimezonefrom.modelsimportQuestiondefcreate_question(question_text,days):创建问题的工具函数timetimezone.now()datetime.timedelta(daysdays)returnQuestion.objects.create(question_textquestion_text,pub_datetime)classQuestionModelTests(TestCase):模型测试deftest_was_published_recently_with_future_question(self):将来日期返回 Falsetimetimezone.now()datetime.timedelta(days30)future_questionQuestion(pub_datetime)self.assertIs(future_question.was_published_recently(),False)deftest_was_published_recently_with_old_question(self):超过一天的过去日期返回 Falsetimetimezone.now()-datetime.timedelta(days1,seconds1)old_questionQuestion(pub_datetime)self.assertIs(old_question.was_published_recently(),False)deftest_was_published_recently_with_recent_question(self):一天内的日期返回 Truetimetimezone.now()-datetime.timedelta(hours23,minutes59,seconds59)recent_questionQuestion(pub_datetime)self.assertIs(recent_question.was_published_recently(),True)classQuestionIndexViewTests(TestCase):首页视图测试deftest_no_questions(self):没有问题时显示适当消息responseself.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],[])deftest_past_question(self):过去的问题显示在首页past_questioncreate_question(question_textPast question.,days-30)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[past_question],)deftest_future_question(self):将来的问题不显示在首页create_question(question_textFuture question.,days30)responseself.client.get(reverse(polls:index))self.assertContains(response,No polls are available.)self.assertQuerySetEqual(response.context[latest_question_list],[])deftest_future_question_and_past_question(self):同时存在过去和将来的问题时只显示过去的past_questioncreate_question(question_textPast question.,days-30)create_question(question_textFuture question.,days30)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[past_question],)deftest_two_past_questions(self):显示多个过去的问题按时间排序question1create_question(question_textPast question 1.,days-30)question2create_question(question_textPast question 2.,days-5)responseself.client.get(reverse(polls:index))self.assertQuerySetEqual(response.context[latest_question_list],[question2,question1],)classQuestionDetailViewTests(TestCase):详情页视图测试deftest_future_question(self):未来问题的详情页返回 404future_questioncreate_question(question_textFuture question.,days5)urlreverse(polls:detail,args(future_question.id,))responseself.client.get(url)self.assertEqual(response.status_code,404)deftest_past_question(self):过去问题的详情页显示问题内容past_questioncreate_question(question_textPast Question.,days-5)urlreverse(polls:detail,args(past_question.id,))responseself.client.get(url)self.assertContains(response,past_question.question_text)polls/views.py更新版fromdjango.httpimportHttpResponseRedirectfromdjango.shortcutsimportget_object_or_404,renderfromdjango.urlsimportreversefromdjango.utilsimporttimezonefromdjango.viewsimportgenericfrom.modelsimportChoice,QuestionclassIndexView(generic.ListView):template_namepolls/index.htmlcontext_object_namelatest_question_listdefget_queryset(self):返回最近发布的5个问题不包括未来的returnQuestion.objects.filter(pub_date__ltetimezone.now()).order_by(-pub_date)[:5]classDetailView(generic.DetailView):modelQuestion template_namepolls/detail.htmldefget_queryset(self):排除尚未发布的问题returnQuestion.objects.filter(pub_date__ltetimezone.now())classResultsView(generic.DetailView):modelQuestion template_namepolls/results.htmldefvote(request,question_id):questionget_object_or_404(Question,pkquestion_id)try:selected_choicequestion.choice_set.get(pkrequest.POST[choice])except(KeyError,Choice.DoesNotExist):returnrender(request,polls/detail.html,{question:question,error_message:You didnt select a choice.,})else:selected_choice.votes1selected_choice.save()returnHttpResponseRedirect(reverse(polls:results,args(question.id,)))静态文件结构polls/ static/ polls/ style.css # 主样式表 images/ background.gif # 背景图片 templates/ polls/ index.html # 包含 {% load static %} 和 CSS 引用 detail.html results.html参考资源Django 6.0 官方文档 - 编写你的第一个 Django 应用第 5 部分Django 6.0 官方文档 - 编写你的第一个 Django 应用第 6 部分
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2423395.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!