这里写目录标题
- 1、自动化测试简介
- (1)自动化测试是什么
- (2)为什么要写测试
- 测试节约你的时间
- 发现错误,预防错误
- 测试使得代码更有吸引力
- 2、基础测试策略
- 3、开始写第一个测试
- (1)首先得有个bug
- (2)创建一个猜测来暴露这个bug
- (3)运行测试
- (4)修复这个bug
- (5)更全面的测试
- 4、测试视图
- (1)针对视图的测试
- (2)Django测试工具之Client
- (3)改善视图代码
- (4)测试新视图
- (5)测试DetailView
- 5、当需要测试的时候,测试用例越多越好
- 6、深入代码测试
1、自动化测试简介
(1)自动化测试是什么
测试在不同层次中都存在。有些测试关注很小的细节(函数返回值是否满足预期),而另一些测试检查对某个软件的一系列操作(某一用户属兔序列是否造成了预期结果),我们使用shell来测试某一方法的功能,或者运行某个应用并输入数据来检查它的行为。
自动化测试是某个系统帮你完成的。当你创建好了一系列测试,每次修改应用代码后,可以自动检查出修改后的代码是否还像预期那样工作。而不需要花费大量时间手动测试。
(2)为什么要写测试
对写复杂项目有用
测试节约你的时间
手动测试浪费时间,要考虑大量数据,但是自动化测试能够帮助我们在几秒钟内完成这件事情
发现错误,预防错误
测试能够帮助我们清晰代码的意图
测试使得代码更有吸引力
测试让其他开发者知道你的代码通过了测试
2、基础测试策略
测试驱动——写代码之前写测试
3、开始写第一个测试
(1)首先得有个bug
现在我们就有一个bug
我们的要求是如果Question是在一天之内发布的,Question.was_published_recently()方法返回True,但是这个方法在Question的pub_date比这个时间还晚的情况下仍然返回True
py manage.py shell
>>> 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
很明显这是一个错误
(2)创建一个猜测来暴露这个bug
我们刚刚手动做的测试就是自动化测试应该做的工作
在 polls/tests.py 中编写以下代码
from django.test import TestCase
import datetime
from django.utils import timezone
from .models import Questionclass QuestionModelTests(TestCase):def test_was_published_recetly_with_future_question(self):time=timezone.now()+datetime.timedelta(days=30)future_question=Question(pub_date=time)self.assertIs(future_question.was_published_recently(),False)
解释一下 assertis 的作用:
future_question.was_published_recently() 调用 was_published_recently 方法,返回一个布尔值。
self.assertIs(future_question.was_published_recently(), False) 断言 future_question.was_published_recently() 的返回值是 False,即检查 was_published_recently 方法的结果是否与 False 是同一个对象。
如果 was_published_recently() 返回 False,断言通过;否则,断言失败,测试报告将显示失败的信息。
(3)运行测试
py manage.py test polls
这个过程中发生了什么呢
(4)修复这个bug
在 polls/models 中
def was_published_recently(self):now=timezone.now()return now-datetime.timedelta(days=1) <= self.pub_date <= now
再次运行测试
现在通过了测试,可以认为之后都不会出现这个错误了
(5)更全面的测试
在 tests.py 中增加
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_time=time)self.assertIs(recent_question.was_published_recently(),True)
4、测试视图
现在还有一个问题是 我们的polls应用对所有应用“一视同仁”,当pub_date为未来的某一天时,这个问题也照常发布,但是应该在未来的那个时刻发布才对
(1)针对视图的测试
为了修复这个bug,我们这次先编写测试,再去写代码,也就是“测试驱动”,其实这两者的顺序不是很重要
在开始之前先来看一下需要用到的工具
(2)Django测试工具之Client
dango提供了一个测试使用的Client 来模拟用户和视图层代码的交互,我们能够在 tests 以及 shell 中使用它
先从shell开始,要做一些在tests 中不是必须的工作:配置测试环境:
(3)改善视图代码
我们创建一个未来的投票也会显示,现在来修复这个问题
def get_queryset(self):return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]
__lte 是 Django 查询语法中的 “小于或等于”(less than or equal to)的表示方法。
(4)测试新视图
def create_question(question_text,days):time=timezone.now()+datetime.timedelta(days=days)return Question.objects.create(question_text=question_text,pub_date=time)class QuestionIndexView(TestCase):def test_no_question(self):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):question=create_question(question_text="Past question",days=-30)response=self.client.get(reverse("polls:index"))self.assertQuerySetEqual(response.context["latest_question_list"],[question],)def test_future_question(self):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_and_past_question(self):question=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],)def test_two_past_questions(self):question1=create_question(question_text="Past question 1.",days=-30)question2=create_question(question_text="Past question 2.",days=-5)response=self.client.get(reverse("polls:index"))self.assertQuerySetEqual(response.context["latest_question_list"],[question1,question2])
assertIs, assertEqual, assertContains, 和 assertQuerySetEqual 是 Django 测试框架中的一些常用断言方法,用于在单元测试中进行各种类型的断言
assertIs:作用:断言两个对象是同一个对象(即,两个对象的引用是相同的)
assertEqual:作用:断言两个对象相等(即,两个对象的值相等)。
测试就是假装一些管理员的输入,然后通过用户端的表现是否符合预期来判断加入的改变是否破坏了原有的系统状态
(5)测试DetailView
对于未来的投票,如果用户输入了正确的url 还是可以访问到它们,所以我们在DetailView里增加一些约束内容
polls.view.py
class DetailView(generic.DetailView):model = Questiontemplate_name = "polls/detail.html"def get_queryset(self):return Question.objects.filter(pub_date__lte=timezone.now())
test.py
class QuestionDetailViewTests(TestCase):def test_future_question(self):future_question=create_question(question_text="Future question.",days=5)url=reverse("polls:detail",args=(future_question.id,))response=self.client.get(url)self.assertEqual(response.status_code,404)def test_past_question(self):past_question=create_question(question_text="Past question.",days=-5)url=reverse("polls:detail",args=(past_question.id,))response=self.client.get(url)self.assertContains(response,past_question.question_text)
5、当需要测试的时候,测试用例越多越好
写完测试就可以忘掉它啦,所以可以让它肆意增长
对于测试有以下建议
- 对每个模型、视图建立单独的 TestClass
- 每个测试方法只测试一个功能
- 给测试起直观的名字
6、深入代码测试
Django中的测试