测试至关重要。 如果没有正确测试您的代码,您将永远不会知道代码现在或将来当代码库发生变化时是否能正常工作。修复因代码库更改引起的问题可能会花费无数时间。更糟糕的是,您甚至可能根本不知道存在问题,直到您的最终用户抱怨为止,这显然不是您想要了解代码中断的方式。
进行适当的测试将帮助 确保如果某个特定功能出现故障,您会知道。测试也使得调试 破坏代码变得更加容易,从而节省时间和金钱。
过去,我确实因为没有针对旧代码库正确测试新功能而失去了工作机会。不要让这种事发生在你身上。认真对待测试。您将对您的代码更有信心,您的雇主也会对您更有信心。它本质上是一份保险单。
测试帮助 您构建良好的代码、查找错误并编写文档。
在这篇文章中,我们将首先查看包括最佳实践的简短介绍,然后再查看一些示例。
Django 测试简介
测试类型
单元测试和集成测试是两种主要类型:
单元测试 是测试一项特定功能的孤立测试。
集成测试 同时,是更大规模的测试,重点关注用户行为并测试整个应用程序。换句话说,集成测试结合了不同的代码功能,以确保它们的行为正确。
专注于单元测试。写了很多这些。与集成测试相比,这些测试更容易编写和调试,并且您拥有的测试越多,需要的集成测试就越少。单元测试应该很快。我们将研究一些加速测试的技术。
也就是说,即使您已经覆盖了单元测试,集成测试有时仍然是必要的,因为集成测试可以帮助捕获代码回归 .
一般来说,测试会导致成功(预期结果)、失败(意外结果)或错误。您不仅需要测试预期结果,还需要测试代码处理意外结果的能力。
最佳实践
如果它可能破裂,则应进行测试。这包括模型、视图、表单、模板、验证器等等。
每次测试通常应该只测试一项功能。
把事情简单化。您不想在其他测试之上编写测试。
每当代码从存储库中拉取或推送时以及在推送到生产环境之前在暂存环境中运行测试。
When upgrading to a newer version of Django:
本地升级,
运行你的测试套件,
修复错误,
PUSH 到存储库和暂存,然后
在交付代码之前再次进行暂存测试。
结构
构建您的测试以适合您的项目。我倾向于将每个应用程序的所有测试放在测试.py 根据我正在测试的内容(例如模型、视图、表单等)对测试进行文件和分组。
您还可以绕过(删除)测试.py 完全归档并在每个应用程序中以这种方式构建您的测试:
└── app_name
└── tests
├── __init__.py
├── test_forms.py
├── test_models.py
└── test_views.py
最后,您可以创建一个单独的测试文件夹,它镜像整个项目结构,放置一个测试.py 每个应用程序文件夹中的文件。
较大的项目应使用后一种结构。如果您知道较小的项目最终会扩展到更大的项目,那么最好也使用后两种结构之一。我喜欢第一个和第三个结构,因为我发现当每个应用程序都可以在一个脚本中查看时,为每个应用程序设计测试会更容易。
第三方包
使用以下包和库来帮助编写和运行测试套件:
django-webtest :使得编写符合最终用户体验的功能测试和断言变得更加容易。将这些测试与 Selenium 测试结合起来,以全面覆盖模板和视图。
覆盖范围 :用于衡量测试的有效性,显示测试覆盖的代码库的百分比。如果您刚刚开始设置单元测试,覆盖范围可以帮助提供有关应测试内容的建议。覆盖率还可以用于将测试变成游戏:例如,我尝试每天增加测试覆盖的代码百分比。
django-发现-runner :如果您以不同的方式组织测试(例如,在测试.py )。因此,如果您将测试组织到单独的文件夹中(如上面的示例所示),则可以使用 discovery-runner 来查找测试。
工厂男孩 ,模特妈妈 , 和嘲笑 : 全部用来代替固定装置 或用于填充测试所需数据的 ORM。夹具和 ORM 都可能很慢,并且在模型发生变化时都需要更新。
例子
在这个基本示例中,我们将测试:
下载 Github 存储库这里 跟随。
设置
安装覆盖范围 并将其添加到您的 INSTALLED_APPS 中:
$ pip install coverage == 3 .6
运行覆盖范围:
$ coverage run manage.py test whatever -v 2
使用详细级别 2,-v 2
,了解更多详情。您还可以使用以下命令立即测试整个 Django 项目:coverage run manage.py test -v 2
.
构建报告以查看应该从哪里开始测试:
打开django15/htmlcov/index.html 查看报告的结果。滚动到报告底部。您可以跳过 virtualenv 文件夹中的所有行。切勿测试任何内置 Python 函数或库,因为它们已经过测试。您可以将 virtualenv 从文件夹中移出,以使报告运行后更清晰。
让我们从测试模型开始。
测试模型
在覆盖范围报告中,单击“任何/型号”的链接。您应该看到这个屏幕:
本质上,该报告表明我们应该测试条目的标题。简单的。
打开测试.py 并添加以下代码:
from django.test import TestCase
from whatever.models import Whatever
from django.utils import timezone
from django.core.urlresolvers import reverse
from whatever.forms import WhateverForm
# models test
class WhateverTest ( TestCase ):
def create_whatever ( self , title = "only a test" , body = "yes, this is only a test" ):
return Whatever . objects . create ( title = title , body = body , created_at = timezone . now ())
def test_whatever_creation ( self ):
w = self . create_whatever ()
self . assertTrue ( isinstance ( w , Whatever ))
self . assertEqual ( w . __unicode__ (), w . title )
这里发生了什么? 我们本质上创建了一个Whatever
对象并测试创建的标题是否与预期标题匹配 - 确实如此。
注意:确保您的函数名称以test_
,这不仅是一个常见的约定,而且还可以让 django-discover-runner 可以找到测试。另外,编写测试all 添加到模型的方法。
重新运行覆盖范围:
$ coverage run manage.py test whatever -v 2
您应该看到以下结果,表明测试已通过:
test_whatever_creation (whatever.tests.WhateverTest) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
然后,如果您再次查看覆盖率报告,模型现在应该为 100%。
测试视图
测试视图有时可能很困难。我通常使用单元测试来检查状态代码,并使用 Selenium Webdriver 来测试 AJAX、Javascript 等。
将以下代码添加到中的WhateverTest类中测试.py :
# views (uses reverse)
def test_whatever_list_view ( self ):
w = self . create_whatever ()
url = reverse ( "whatever.views.whatever" )
resp = self . client . get ( url )
self . assertEqual ( resp . status_code , 200 )
self . assertIn ( w . title , resp . content )
这里我们从客户端获取 URL,将结果存储在变量中resp
然后测试我们的断言。首先,我们测试响应代码是否为200,然后我们测试实际的响应返回。你应该得到
结果如下:
test_whatever_creation (whatever.tests.WhateverTest) ... ok
test_whatever_list_view (whatever.tests.WhateverTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.052s
OK
再次运行您的报告。您现在应该看到“whatever/views”的链接,显示以下结果:
您还可以编写测试来确保某些事情失败。例如,如果用户需要登录才能创建新对象,如果实际上创建对象失败,则测试将成功。
让我们看一下硒的快速测试:
# views (uses selenium)
import unittest
from selenium import webdriver
class TestSignup ( unittest . TestCase ):
def setUp ( self ):
self . driver = webdriver . Firefox ()
def test_signup_fire ( self ):
self . driver . get ( "http://localhost:8000/add/" )
self . driver . find_element_by_id ( 'id_title' ) . send_keys ( "test title" )
self . driver . find_element_by_id ( 'id_body' ) . send_keys ( "test body" )
self . driver . find_element_by_id ( 'submit' ) . click ()
self . assertIn ( "http://localhost:8000/" , self . driver . current_url )
def tearDown ( self ):
self . driver . quit
if __name__ == '__main__' :
unittest . main ()
安装硒:
$ pip install selenium == 2 .33.0
运行测试。 Firefox 应加载(如果您已安装)并运行测试。然后我们断言提交时加载了正确的页面。您还可以检查以确保新对象已添加到数据库中。
添加以下方法:
def test_valid_form ( self ):
w = Whatever . objects . create ( title = 'Foo' , body = 'Bar' )
data = { 'title' : w . title , 'body' : w . body ,}
form = WhateverForm ( data = data )
self . assertTrue ( form . is_valid ())
def test_invalid_form ( self ):
w = Whatever . objects . create ( title = 'Foo' , body = '' )
data = { 'title' : w . title , 'body' : w . body ,}
form = WhateverForm ( data = data )
self . assertFalse ( form . is_valid ())
请注意我们如何从 JSON 生成表单数据。这是一个固定装置。
您现在应该已经通过了 5 项测试:
test_signup_fire (whatever.tests.TestSignup) ... ok
test_invalid_form (whatever.tests.WhateverTest) ... ok
test_valid_form (whatever.tests.WhateverTest) ... ok
test_whatever_creation (whatever.tests.WhateverTest) ... ok
test_whatever_list_view (whatever.tests.WhateverTest) ... ok
----------------------------------------------------------------------
Ran 5 tests in 12.753s
OK
您还可以编写测试来断言是否根据表单本身中的验证器显示特定错误消息。
测试 API
第一的,您可以访问API 从这个网址:http://localhost:8000/api/whatever/?format=json 。这是一个简单的设置,因此测试也相当简单。
安装lxml并解散XML:
$ pip install lxml == 3 .2.3
$ pip install defusedxml == 0 .4.1
添加以下测试用例:
from tastypie.test import ResourceTestCase
class EntryResourceTest ( ResourceTestCase ):
def test_get_api_json ( self ):
resp = self . api_client . get ( '/api/whatever/' , format = 'json' )
self . assertValidJSONResponse ( resp )
def test_get_api_xml ( self ):
resp = self . api_client . get ( '/api/whatever/' , format = 'xml' )
self . assertValidXMLResponse ( resp )
我们只是断言我们在每种情况下都会得到回应。
下次
在下一个教程中,我们将研究一个更复杂的示例,并使用 model_mommy 生成测试数据。同样,您可以从以下位置获取代码回购协议 .
有什么要补充的吗?请在下面发表评论。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)