个人学习笔记,参考 django 官方文档:https://docs.djangoproject.com/zh-hans/3.2/
一、自动化测试 测试代码,就是检查你的代码能否正常运行(废话)
实话说,就目前为止,我写程序都是运行——>报错——>然后看报错信息——>print
输入输出这样去测试的。但是项目毕竟是一个整体的项目,这样测试未免太不专业了。
自动化测试具有以下优点:
测试将节约你的时间
测试不仅能发现错误,而且能预防错误
测试是你的代码更有吸引力
测试有利于团队协作
听起来不错,那就试试吧!
1、首先得有个 BUG 按照我们之前写的这个应用逻辑,当我们访问index
这个页面时,我们应该会得到最近发布的五条投票,如果有五条的话。
但是现在有一个小 bug 就是,如果我们的投票是定时到明天发布的,我们的想法是用户明天才能看到这条投票,index
页面不应该显示这条数据,但按照目前的逻辑,index
会立马显示这条数据。
注意
:上面描述的确实是一个 BUG,但是还有一个重要的 BUG,就是之前我们再写数据模型时,我们根本没定义任何方法来显示一天内的数据。原谅我没有看到这个要求:Question 是在一天之内发布的。
下面是 model 层现在的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from django.db import modelsimport datetimefrom django.utils import timezoneclass Question (models.Model ): question_text = models.CharField(max_length=200 ) pub_date = models.DateTimeField('date published' ) def was_published_recently (self ): return self.pub_date >= timezone.now() - datetime.timedelta(days=1 ) def __str__ (self ): return self.question_text 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
2、暴露这个 BUG 文字描述这个 BUG 确实有点苍白无力,下面我们用 python
manage.py shell
命令来暴露这个 BUG。
在项目根目录下打开终端,输入python manage.py shell
进入交互式编译环境。
1 2 3 4 5 6 7 8 >>> 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
很显然现在应用会公开未来的投票,所以我们要修复这个 BUG,哦对了,这不是一个自动化测试,我们只是在交互式环境下测试的,我们应该把他写成一个文件,当项目运行时,执行自动化测试。
3、自动化测试 按照惯例,Django 应用的测试应该写在应用的 tests.py
文件里。测试系统会自动的在所有以 tests
开头的文件里寻找并执行测试代码。
polls/tests.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from django.test import TestCaseimport datetimefrom django.utils import timezonefrom .models import Questionclass QuestionModelTests (TestCase ): def test_was_published_recently_with_future_question (self ): """ 未来的一个时间他的返回值应该是False """ time = timezone.now() + datetime.timedelta(days=30 ) future_question = Question(pub_date=time) self.assertIs(future_question.was_published_recently(), False )
4、运行测试 在终端执行测试命令python manage.py test polls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PS J:\study_django\mysite> python manage.py test polls Creating test database for alias 'default' ... System check identified no issues (0 silenced). E ====================================================================== ERROR: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests) 未来的一个时间他的返回值应该是False ---------------------------------------------------------------------- Traceback (most recent call last): File "J:\study_django\mysite\polls\tests.py" , line 19 , in test_was_published_recently_with_future_question self.assertIs(future_question.was_published_recently(), False ) AttributeError: 'Question' object has no attribute 'was_published_recently' ---------------------------------------------------------------------- Ran 1 test in 0.003 s FAILED (errors=1 ) Destroying test database for alias 'default' ... PS J:\study_django\mysite>
以下是自动化测试的运行过程:
python manage.py test polls
将会寻找 polls
应用里的测试代码
它找到了 django.test.TestCase 的一个子类
它创建一个特殊的数据库供测试使用
它在类中寻找测试方法——以 test
开头的方法。
在 test_was_published_recently_with_future_question
方法中,它创建了一个 pub_date
值为 30 天后的 Question
实例。
接着使用 assertls()
方法,发现 was_published_recently()
返回了 True
,而我们期望它返回 False
。
5、修复这个 BUG 当 pub_date
为未来某天时, Question.was_published_recently()
应该返回 False
。修改 models.py
里的方法,让它只在日期是过去式的时候才返回 True
:
polls/models.py
1 2 3 4 5 ... def was_published_recently (self ): now = timezone.now() return now - datetime.timedelta(days=1 ) <= self.pub_date <= now ...
6、更全面的测试 最好对过去、最近、将来都进行测试。于是把测试代码修改如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from django.test import TestCaseimport datetimefrom django.utils import timezonefrom .models import Questionclass QuestionModelTests (TestCase ): def test_was_published_recently_with_future_question (self ): """ 未来的一个时间他的返回值应该是False """ 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 ): """ 最近一天的 """ 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 ): """ 过去的 """ 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 )
7、其他方案 还有一个解决思路就是,我们不应该显示未来的投票,那么我们在视图显示时,就应该过滤掉过去
和未来
这个我没有继续研究。
二、静态文件的引入 首先,在 polls
目录下创建一个名为 static
的目录。Django 将在该目录下查找静态文件,这种方式和 Diango 在 polls/templates/
目录下查找 template 的方式类似。
1、新建 css 样式 在静态文件目录下新建样式。
static/style.css
2、引入静态文件 在模板中引入静态文件。
index.html
1 2 3 {% load static %} <link rel ="stylesheet" type ="text/css" href ="{% static 'style.css' %}" />
3、运行项目