Django实战(10):单元测试
尽早进行单元测试(UnitTest)是比较好的做法,极端的情况甚至强调“测试先行”。现在我们已经有了第一个model类和Form类,是时候开始写测试代码了。
Django支持python的单元测试(unit test)和文本测试(doc test),我们这里主要讨论单元测试的方式。这里不对单元测试的理论做过多的阐述,假设你已经熟悉了下列概念:test suite, test case, test/test action, test data, assert等等。
在单元测试方面,Django继承python的unittest.TestCase实现了自己的django.test.TestCase,编写 测试用 例通常从这里开始。测试代码通常位于app的tests.py文件中(也可以在models.py中编写,但是我不建议这样做)。在Django生成的 depotapp中,已经包含了这个文件,并且其中包含了一个测试用例的样例:
depot/depotapp/tests.py
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that + always equals .
"""
self.assertEqual( + , )
你可以有几种方式运行单元测试:
python manage.py test:执行所有的测试用例
python manage.py test app_name, 执行该app的所有测试用例
python manage.py test app_name.case_name: 执行指定的测试用例
用第三中方式执行上面提供的样例,结果如下:
$ python manage.py test depotapp.SimpleTest
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.012s
OK
Destroying test database for alias 'default'...
你可能会主要到,输出信息中包括了创建和删除数据库的操作。为了避免测试数据造成的影响,测试过程会使用一个单独的数据库,关于如何指定测试数据库
的细节,请查阅Django文档。在我们的例子中,由于使用sqlite数据库,Django将默认采用内存数据库来进行测试。
下面就让我们来编写测试用例。在《Agile Web Development with Rails 4th》中,7.2节,最终实现的ProductTest代码如下:
class ProductTest < ActiveSupport::TestCase
test "product attributes must not be empty"do
product = Product.new
assert product.invalid?
assert product.errors[:title].any?
assert product.errors[:description].any?
assert product.errors[:price].any?
assert product.errors[:image_url].any?
end
test "product price must be positive"do
product = Product.new(:title => "My Book Title",
:description => "yyy",
:image_url => "zzz.jpg")
product.price = -
assert product.invalid?
assert_equal "must be greater than or equal to 0.01",
product.errors[:price].join('; ')
product.price =
assert product.invalid?
assert_equal "must be greater than or equal to 0.01",
product.errors[:price].join('; ')
product.price =
assert product.valid?
end
def new_product(image_url)
Product.new(:title => "My Book Title",
:description => "yyy",
:price => ,
:image_url => image_url)
end
test "image url"do
ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg
http://a.b.c/x/y/z/fred.gif }
bad = %w{ fred.doc fred.gif/more fred.gif.more }
ok.eachdo |name|
assert new_product(name).valid?, "#{name} shouldn't be invalid"
end
bad.eachdo |name|
assert new_product(name).invalid?, "#{name} shouldn't be valid"
end
end
test "product is not valid without a unique title"do
product = Product.new(:title => products(:ruby).title,
:description => "yyy",
:price => ,
:image_url => "fred.gif")
assert !product.save
assert_equal "has already been taken", product.errors[:title].join('; ')
end
test "product is not valid without a unique title - i18n"do
product = Product.new(:title => products(:ruby).title,
:description => "yyy",
:price => ,
:image_url => "fred.gif")
assert !product.save
assert_equal I18n.translate('activerecord.errors.messages.taken'),
product.errors[:title].join('; ')
end
end
对Product测试的内容包括:
1.title,description,price,image_url不能为空;
2. price必须大于零;
3. image_url必须以jpg,png,jpg结尾,并且对大小写不敏感;
4. titile必须唯一;
让我们在Django中进行这些测试。由于ProductForm包含了模型校验和表单校验规则,使用ProductForm可以很容易的实现上述测试:
depot/depotapp/tests.py
#/usr/bin/python
#coding: utf8
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
from forms import ProductForm
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that + always equals .
"""
self.assertEqual( + , )
class ProductTest(TestCase):
def setUp(self):
self.product = {
'title':'My Book Title',
'description':'yyy',
'image_url':'http://google.com/logo.png',
'price':
}
f = ProductForm(self.product)
f.save()
self.product['title'] = 'My Another Book Title'
#### title,description,price,image_url不能为空
def test_attrs_cannot_empty(self):
f = ProductForm({})
self.assertFalse(f.is_valid())
self.assertTrue(f['title'].errors)
self.assertTrue(f['description'].errors)
self.assertTrue(f['price'].errors)
self.assertTrue(f['image_url'].errors)
#### price必须大于零
def test_price_positive(self):
f = ProductForm(self.product)
self.assertTrue(f.is_valid())
self.product['price'] =
f = ProductForm(self.product)
self.assertFalse(f.is_valid())
self.product['price'] = -
f = ProductForm(self.product)
self.assertFalse(f.is_valid())
self.product['price'] =
#### image_url必须以jpg,png,jpg结尾,并且对大小写不敏感;
def test_imgae_url_endwiths(self):
url_base = 'http://google.com/'
oks = ('fred.gif', 'fred.jpg', 'fred.png', 'FRED.JPG', 'FRED.Jpg')
bads = ('fred.doc', 'fred.gif/more', 'fred.gif.more')
for endwith in oks:
self.product['image_url'] = url_base+endwith
f = ProductForm(self.product)
self.assertTrue(f.is_valid(),msg='error when image_url endwith '+endwith)
for endwith in bads:
self.product['image_url'] = url_base+endwith
f = ProductForm(self.product)
self.assertFalse(f.is_valid(),msg='error when image_url endwith '+endwith)
self.product['image_url'] = 'http://google.com/logo.png'
### titile必须唯一
def test_title_unique(self):
self.product['title'] = 'My Book Title'
f = ProductForm(self.product)
self.assertFalse(f.is_valid())
self.product['title'] = 'My Another Book Title'
然后运行 python manage.py test depotapp.ProductTest。如同预想的那样,测试没有通过:
Creating test database for alias 'default'...
.F..
======================================================================
FAIL: test_imgae_url_endwiths (depot.depotapp.tests.ProductTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/holbrook/Documents/Dropbox/depot/../depot/depotapp/tests.py", line 65, in test_imgae_url_endwiths
self.assertTrue(f.is_valid(),msg='error when image_url endwith '+endwith)
AssertionError: False is not True : error when image_url endwith FRED.JPG
----------------------------------------------------------------------
Ran 4 tests in 0.055s
FAILED (failures=1)
Destroying test database for alias 'default'...
因为我们之前并没有考虑到image_url的图片扩展名可能会大写。修改ProductForm的相关部分如下:
def clean_image_url(self):
url = self.cleaned_data['image_url']
ifnot endsWith(url.lower(), '.jpg', '.png', '.gif'):
raise forms.ValidationError('图片格式必须为jpg、png或gif')
return url
然后再运行测试:
$ python manage.py test depotapp.ProductTest
Creating test database for alias 'default'...
....
----------------------------------------------------------------------
Ran 4 tests in 0.060s
OK
Destroying test database for alias 'default'...
测试通过,并且通过单元测试,我们发现并解决了一个bug。
Django实战(10):单元测试的更多相关文章
- Django 1.10中文文档-执行查询
Django 1.10中文文档: https://github.com/jhao104/django-chinese-doc 只要创建好 数据模型, Django 会自动为生成一套数据库抽象的API, ...
- Python单元测试简介及Django中的单元测试
Python单元测试简介及Django中的单元测试 单元测试负责对最小的软件设计单元(模块)进行验证,unittest是Python自带的单元测试框架. 单元测试与功能测试都是日常开发中必不可少的部分 ...
- “全能”选手—Django 1.10文档中文版Part1
本文是博主翻译的Django1.10版本官方文档的第一部分,如时间充裕,争取一直翻译下去,经验不足,或有错漏,敬请指正. 另外对于公开文档进行翻译的版权问题不是很清楚,如有侵权请联系我! 另外,要转载 ...
- django 1.10 CSRF验证失败的解决过程
最近工作闲,没事自学django,感觉这个最烦的就是各版本提供的api函数经常有变化,不是取消了就是参数没有了,网上搜到的帖子也没说明用的是什么版本的django,所以经常出现搬运过来的代码解决不了问 ...
- “全能”选手—Django 1.10文档中文版Part3
Django 1.10官方文档的入门教程已经翻译完毕,后续的部分将不会按照顺序进行翻译,而是挑重点的先翻译. 有兴趣的可以关注我的博客. 第一部分传送门 第二部分传送门 第四部分传送门 3.2 模型和 ...
- Django 1.10 找不到静态资源解决方法
测试版本:Django 1.10 问题:Django项目找不到静态资源 解决方法: 1.首先你需要在自己的app下面创建2个目录 static 和 templates 树形结构如下(DjangoPr ...
- django中使用Profile扩展User模块(基于django 1.10版本下)
版本:Django 1.10.1(其他版本可能有不同的实现好解决办法) 参考官方文档:https://docs.djangoproject.com/en/1.10/topics/auth/custom ...
- Django 1.10中文文档—第一个Django应用Part1
在本教程中,我们将引导您完成一个投票应用程序的创建,它包含下面两部分: 一个可以进行投票和查看结果的公开站点: 一个可以进行增删改查的后台admin管理界面: 我们假设你已经安装了Django.您可以 ...
- Django 1.10中文文档-第一个应用Part2-模型和管理站点
本教程继续Part1.我们将设置数据库,创建您的第一个模型,并快速介绍Django的自动生成的管理网站. 数据库设置 现在,编辑mysite/settings.py.它是一个用模块级别变量表示Djan ...
- Django 1.10中文文档-聚合
Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法. 然而,有时候你要获取的值需要根据一组对象聚合后才能得到. 这个主题指南描述了如何使用Django的查询来生成和返 ...
随机推荐
- 深入分析Parquet列式存储格式
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目,最新的版本是1.8.0. 列式存储 列式存 ...
- 经典幻灯片插件Swiper
照着写的demo,搞清楚什么叫分页器Pagination,什么叫nav,搞清楚DOM结构,container,wrapper之类的,就能写了.效果掉渣天! <!DOCTYPE html> ...
- windows git gui右键sublime打开当前文件编辑
git安装目录\Git\libexec\git-core\git-gui.tcl的 proc create_common_diff_popup 下追加: $ctxm add command \ -la ...
- 深度优先搜索(DFS)----------------Tju_Oj_3517The longest athletic track
这个题主要考察对树的操作,主要思想是DFS或者BFS,其次是找树的直径方法(既要运用两次BFS/DFS),最后作为小白,还练习了vector的操作. DFS框架伪码: bool DSF(Node on ...
- AS中一些不经常用到的快捷键
1 书签 添加/移除书签 Ctrl+shift+F11 展示书签 shift+F11 下一个书签 shift+加号 上一个书签 shift+减号 2 折叠/展开代码块 展开代码块 ctrl+加号 ...
- C# Java 加密解密
C# AES加密解密 public static string Encrypt(string key, string clearText) { byte[] clearBytes = Encoding ...
- gnuplot生成MySQL QPS图形
1.建立MySQL QPS执行脚本 #!/bin/bash mysqladmin -uroot -p' extended-status -i1|awk \ 'BEGIN{flag=0; print & ...
- C# 日文网址转punnycode
Uri uri = new Uri(url); IdnMapping idn = new IdnMapping();url= url.Replace(uri.Host, idn.GetAscii(ur ...
- Oracle 函数 “自动生成订单号”
create or replace function get_request_code return varchar2 AS --函数的作用:自动生成订单号 v_mca_no mcode_apply_ ...
- opencv之dft及mat类型转换
跑实验时用到dft这个函数,根据教程,需要先将其扩充到最优尺寸,但我用逆变换后发现得到的mat的维数竟然不一样.因此还是不要扩展尺寸了. 参考:http://www.xpc-yx.com/2014/1 ...