个人学习笔记,参考 django 官方文档:https://docs.djangoproject.com/zh-hans/3.2/
一、自动化测试
测试代码,就是检查你的代码能否正常运行(废话)
实话说,就目前为止,我写程序都是运行——>报错——>然后看报错信息——>print
输入输出这样去测试的。但是项目毕竟是一个整体的项目,这样测试未免太不专业了。
自动化测试具有以下优点:
- 测试将节约你的时间
- 测试不仅能发现错误,而且能预防错误
- 测试是你的代码更有吸引力
- 测试有利于团队协作
听起来不错,那就试试吧!
1、首先得有个 BUG
按照我们之前写的这个应用逻辑,当我们访问index
这个页面时,我们应该会得到最近发布的五条投票,如果有五条的话。
但是现在有一个小 bug 就是,如果我们的投票是定时到明天发布的,我们的想法是用户明天才能看到这条投票,index
页面不应该显示这条数据,但按照目前的逻辑,index
会立马显示这条数据。
注意
:上面描述的确实是一个 BUG,但是还有一个重要的 BUG,就是之前我们再写数据模型时,我们根本没定义任何方法来显示一天内的数据。原谅我没有看到这个要求:Question 是在一天之内发布的。
下面是 model 层现在的状态。
代码语言:javascript复制# django框架的接口模块,默认已经引入
from django.db import models
import datetime
from django.utils import timezone
# Create your models here.
# 创建了一个“问题”类(表),表里有两个字段。
class 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)
# python魔法方法,显示调用该对象的时候的返回内容
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)
# python魔法方法,显示调用该对象的时候的返回内容
def __str__(self):
return self.choice_text
2、暴露这个 BUG
文字描述这个 BUG 确实有点苍白无力,下面我们用 python
manage.py shell
命令来暴露这个 BUG。
在项目根目录下打开终端,输入python manage.py shell
进入交互式编译环境。
>>> 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
# Django自带的测试接口,默认已经引入
from django.test import TestCase
# Create your tests here.
# 引入datatime模块
import datetime
from django.utils import timezone
# 引入我们的数据表
from .models import Question
class 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
PS J:study_djangomysite> 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_djangomysitepollstests.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.003s
FAILED (errors=1)
Destroying test database for alias 'default'...
PS J:study_djangomysite>
以下是自动化测试的运行过程:
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
...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
...
6、更全面的测试
最好对过去、最近、将来都进行测试。于是把测试代码修改如下。
代码语言:javascript复制# Django自带的测试接口,默认已经引入
from django.test import TestCase
# Create your tests here.
# 引入datatime模块
import datetime
from django.utils import timezone
# 引入我们的数据表
from .models import Question
class 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
li a {
color: green;
}
2、引入静态文件
在模板中引入静态文件。
index.html
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}" />