赵走x博客
网站访问量:151534
首页
书籍
软件
工具
古诗词
搜索
登录
17、部署 Django 项目
16、自动化测试
14、jQuery 和 Django
13、Webhose 搜索
12、集成 Bootstrap
11、使用 Django-Registration-Redux
10、cookie 和会话
9、用户身份验证
8、模板进阶
7、表单
6、模型、模板和视图
5、模型与数据库
4、模板和媒体文件
3、Django 基础
2、前期准备工作
1、导言
16、自动化测试
资源编号:76323
Tango with Django
Python Web
热度:88
编写测试是个好习惯,这样能确保软件稳固可靠。当然,有时大部分时间都用在实现功能上,没 有多少时间过问软件运行得如何,或者过于自大,确信自己写出的功能一定能用。
编写测试是个好习惯,这样能确保软件稳固可靠。当然,有时大部分时间都用在实现功能上,没 有多少时间过问软件运行得如何,或者过于自大,确信自己写出的功能一定能用。 编写测试的原因有很多,Django 官方教程列出了几点: ❏ 测试能节省时间:修改复杂的系统时可能会出现意外失误。 ❏ 测试不仅能找出问题,还能避免问题的发生:测试能指出哪些代码不符合预期。 ❏ 测试能让代码更具魅力:Django 的原始开发者之一 Jacob Kaplan-Moss 说过,“没有测试的 代码先天不足”。 ❏ 测试能协助团队协作,防止团队中的成员不小心破坏功能。 根据 Python 指南,编写测试时要遵守一些基本规则。下面是其中几条重要的规则: ❏ 测试应该针对具体的小功能 ❏ 测试应该有明确的目的 ❏ 测试应该独立 ❏ 在编写代码之前,提交和推送代码之前要运行测试 ❏ 最好创建一个钩子,在推送代码时运行测试 ❏ 测试的名字不怕长,要明确表明意图 # 17.1 运行测试 Django 集成了测试功能。可以执行下述命令测试 Rango 应用: ``` $ python manage.py test rango Creating test database for alias 'default'... ---------------------------------------------------------------------Ran 0 tests in 0.000s OK Destroying test database for alias 'default'... ``` 这个命令运行 Rango 应用中的所有测试。目前,我们的应用还没什么测试。不信打开 rango/ tests.py 文件看看,里面只有一个导入语句。每次创建应用,Django 都会自动生成这样一个文件, 鼓励你编写测试。 从输出中可以看到,提到了一个名为“default”的数据库。运行测试时,Django 会创建一个临时数 据库,供测试填充数据和执行操作。这样,测试时使用的数据库就与线上数据库隔离开了。 # 17.2 测试模型 下面我们来编写一个测试。对 Category 模型的各属性来说,我们希望查看次数的值为零或正数, 难道次数能小于零?为了测试这个限制,把下述代码写入 rango/tests.py 文件: ``` from django.test import TestCase from rango.models import Category class CategoryMethodTests(TestCase): def test_ensure_views_are_positive(self): """ ensure_views_are_positive 函数在分类的查看次数 为零或正数时应该返回 True """ cat = Category(name='test',views=-1, likes=0) cat.save() self.assertEqual((cat.views >= 0), True) ``` 如果你以前没写过测试,首先注意,测试继承自 TestCase 类。其中的方法也有约定,名称以 `test_ `开头的方法都是测试用例,里面有一些断言(assertion)。这里,我们使用 assertEqual() 方法判断两个值是否相等。此外还有很多断言方法可用(例如 assertItemsEqual、 assertListEqual、assertDictEqual,等等),详情参见文档(Python 2 版,Python 3 版)。Django 的测试机制源自 Python,在此基础上还额外提供了一些断言方法。 下面运行测试: ``` $ python manage.py test rango Creating test database for alias 'default'... F ====================================================================== FAIL: test_ensure_views_are_positive (rango.tests.CategoryMethodTests) ---------------------------------------------------------------------Traceback (most recent call last): File "/Users/leif/Code/tango_with_django_project_19/rango/tests.py", line 12, in test_ensure_views_are_positive self.assertEqual((cat.views>=0), True) AssertionError: False != True ---------------------------------------------------------------------Ran 1 test in 0.001s FAILED (failures=1) ``` 可以看到,这个测试失败了。这是因为 Category 模型并没有检查查看次数的值是否小于零。我们 确实想保证这个属性的值不为负数,因此要修改模型,加入这一限制。请在 Category 模型的 save() 方法中添加一些代码,检查 views 属性的值。这里只需检查 self.views 的值是否小于零就 足够了。 修改模型之后再运行测试,看看能不能通过。如果不能,再修改。 下面再编写一个测试,确保别名的格式正确,即以连字符连接各词,而且全为小写字母。把下述 代码添加到 rango/tests.py 文件中: ``` def test_slug_line_creation(self): """ slug_line_creation 确保添加分类时创建的别名格式是正确的 例如 "Random Category String" -> "random-category-string" """ cat = cat('Random Category String') cat.save() self.assertEqual(cat.slug, 'random-category-string') ``` 现在的代码能让这个测试通过吗? # 17.3 测试视图 前一节编写的测试用于确保模型中数据的完整性。Django 还为视图提供了测试机制,使用一个模 拟客户端通过 URL 访问 Django 视图。在视图测试中可以访问响应(包括 HTML)和上下文字 典。 下面编写一个测试,确认 Category 模型为空时,首页会显示“There are no categories present”消 息。 ``` from django.core.urlresolvers import reverse class IndexViewTests(TestCase): def test_index_view_with_no_categories(self): """ 如果没有分类,应该显示一条提示消息 """ response = self.client.get(reverse('index')) self.assertEqual(response.status_code, 200) self.assertContains(response, "There are no categories present.") self.assertQuerysetEqual(response.context['categories'], []) ``` 首先,Django TestCase 能访问 client 对象,请求就是通过它发送的。这个测试使用 reverse 函数 查询首页的 URL,然后访问那个页面,得到 response 对象。这个测试检查了几件事:首页能不 能成功加载,响应 HTML 中是否包含“There are no categories present.”这句话,以及上下文字典中 的分类列表是否为空。别忘了,运行测试时会创建一个新数据库,但是默认没有填充任何数据。 下面添加几个分类,然后再测试视图。首先定义一个辅助方法: ``` from rango.models import Category def add_cat(name, views, likes): c = Category.objects.get_or_create(name=name)[0] c.views = views c.likes = likes c.save() return c ``` 然后在 IndexViewTests 类中再添加一个测试方法: ``` def test_index_view_with_categories(self): """ 确保首页显示了分类 """ add_cat('test',1,1) add_cat('temp',1,1) add_cat('tmp',1,1) add_cat('tmp test temp',1,1) response = self.client.get(reverse('index')) self.assertEqual(response.status_code, 200) self.assertContains(response, "tmp test temp") num_cats =len(response.context['categories']) self.assertEqual(num_cats , 4) ``` 这个测试先向数据库中填充 4 个分类,然后检查页面中有没有“tmp test temp”文本,以及分类数量 是不是 4 个。注意,这里做了三项检查,但是同属一个测试。 # 17.4 测试渲染的页面 使用 Django 的测试客户端和(或)Selenium 还可以启动应用,以编程的方式测试 HTML 页面中 的 DOM 元素。这里不再举例说明。 # 17.5 测试覆盖度 覆盖度衡量有多少代码已被测试,还有多少尚未得到测试。可以安装 coverage 包(pip install coverage),让它为我们统计覆盖度。安装好之后,执行下述命令: ``` $ coverage run --source='.' manage.py test rango ``` 这个命令检查 Rango 应用的所有测试,汇总覆盖度数据。若想查看覆盖度报告,执行下述命令:  从上述报告可以看出,很多重要的代码还没有测试,例如 rango/views 中的视图。[coverage 包](http://nedbatchelder.com/code/coverage/ "coverage 包")功能 丰富,能协助你编写更全面的测试。