基本知识
测试是一种简单的日常生活,用于检测代码操作。测试操作可以处于水平。有些可能只是测试细小部分如某个模型方法,然而其他的检测软件的整理操作,这和之前的在教程2中使用的测试没什么区别,使用shell测量方法,运行应用程序以及输入数据检测行为。自动化测试的区别是测试工作是由系统为你做的。一旦你编写好了测试集,当你对app做出调整时你就可以检测代码是否符合初期目标。
测试有以下好处:
节省时间,鉴别问题并阻止问题,让代码更有吸引力,促进团队合作
基本测试策略
有些开发者遵循“测试驱动开发”的原理,描述一个问题,然后写代码解决它。
有些新人可能会先写一些代码然后发现应该写一些测试。或许早一些写测试更好,但任何时候开始都不会迟。
有时候很难弄清从哪里开始写测试,那么下次你做出改变时就应该编写测试代码,或者是你添加了一个新特性,或者是修复了一个bug。
测试实践
按照如下方式测试was_published_recently,创建一个问题question , 其创建时间是未来
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> # create a Question instance with pub_date 30 days in the future
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> # was it published recently?
>>> future_question.was_published_recently()
True
以上是基于shell的测试,现在我们编写自动化测试。我们可以在Polls/tests.py中编写如下测试
import datetime
from django.utils import timezone
from django.test import TestCase
from .models import Question
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
运行测试
python manage.py test polls
过程如下:
1.寻找poll应用程序中的测试
2.发现django.test.TestCase的子类
3.为了测试需要创建特殊的数据库
4.寻找测试方法,名字以test开头
5.执行测试方法,创建了Question实例。使用assertIs方法,函数返回的是True,而我们想要的是False。
修改模型,修复bug
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
datetime.timedelta表示一个时间区间
class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
所有参数都是可选的,默认为0
当然我们可以为此方法写更综合的测试。这里我们先写了一个测试代码,然后去修复这个bug,这就是一个简单的测试驱动开发实例。
测试视图
django测试客户端:在视图级别模拟用户与代码的交互
如果使用shell测试,我们需要:
安装模板渲染器,允许我们测试响应的更多属性。它不会创建数据库,因此我们之后的测试是基于已有的数据库
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
导入测试客户端
>>> from django.test import Client
>>> client = Client()
接着
response = client.get(reverse('polls:index'))
response.status_code
response.content
response.context['latest_question_list']
改善视图
修改IndexView,过滤未来的Question
from django.utils import timezone
def get_queryset(self):
"""
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]
为新view创建自动化测试
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'],
['<Question: Past question.>']
)
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'],
['<Question: Past question.>']
)
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'],
['<Question: Past question 2.>', '<Question: Past question 1.>']
)
与之前assertIs类似的方法有
self.assertEqual(response.status_code, 200)
self.assertContains(response, “No polls are available.”)
self.assertQuerysetEqual(response.context[‘latest_question_list’], [])。
多考虑可能出现的情况,并为此编写测试代码。测试越多越好,为了测试便于管理,我们需要遵循如下原则:
1.为每个模型或视图建立单独的测试类
2.为每种情况建立单独的测试方法
3.测试方法基于方法命名