对Python的Django框架中的项目进行单元测试的方法
作者:心内求法 发布时间:2021-02-23 03:17:04
Python中的单元测试
我们先来回顾一下Python中的单元测试方法。
下面是一个 Python的单元测试简单的例子:
假如我们开发一个除法的功能,有的同学可能觉得很简单,代码是这样的:
def division_funtion(x, y):
return x / y
但是这样写究竟对还是不对呢,有些同学可以在代码下面这样测试:
def division_funtion(x, y):
return x / y
if __name__ == '__main__':
print division_funtion(2, 1)
print division_funtion(2, 4)
print division_funtion(8, 3)
但是这样运行后得到的结果,自己每次都得算一下去核对一遍,很不方便,Python中有 unittest 模块,可以很方便地进行测试,详情可以文章最后的链接,看官网文档的详细介绍。
下面是一个简单的示例:
import unittest
def division_funtion(x, y):
return x / y
class TestDivision(unittest.TestCase):
def test_int(self):
self.assertEqual(division_funtion(9, 3), 3)
def test_int2(self):
self.assertEqual(division_funtion(9, 4), 2.25)
def test_float(self):
self.assertEqual(division_funtion(4.2, 3), 1.4)
if __name__ == '__main__':
unittest.main()
我简单地写了三个测试示例(不一定全面,只是示范,比如没有考虑除数是0的情况),运行后发现:
F.F
======================================================================
FAIL: test_float (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/tu/YunPan/mydivision.py", line 16, in test_float
self.assertEqual(division_funtion(4.2, 3), 1.4)
AssertionError: 1.4000000000000001 != 1.4
======================================================================
FAIL: test_int2 (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/tu/YunPan/1.py", line 13, in test_int2
self.assertEqual(division_funtion(9, 4), 2.25)
AssertionError: 2 != 2.25
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=2)
汗!发现了没,竟然两个都失败了,测试发现:
4.2除以3 等于 1.4000000000000001 不等于期望值 1.4
9除以4等于2,不等于期望的 2.25
下面我们就是要修复这些问题,再次运行测试,直到运行不报错为止。
譬如根据实际情况,假设我们只需要保留到小数点后6位,可以这样改:
def division_funtion(x, y):
return round(float(x) / y, 6)
再次运行就不报错了:
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Django中的单元测试
尽早进行单元测试(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 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
你可以有几种方式运行单元测试:
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 = -1
assert product.invalid?
assert_equal "must be greater than or equal to 0.01",
product.errors[:price].join('; ')
product.price = 0
assert product.invalid?
assert_equal "must be greater than or equal to 0.01",
product.errors[:price].join('; ')
product.price = 1
assert product.valid?
end
def new_product(image_url)
Product.new(:title => "My Book Title",
:description => "yyy",
:price => 1,
: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 => 1,
: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 => 1,
: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 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
class ProductTest(TestCase):
def setUp(self):
self.product = {
'title':'My Book Title',
'description':'yyy',
'image_url':'http://google.com/logo.png',
'price':1
}
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'] = 0
f = ProductForm(self.product)
self.assertFalse(f.is_valid())
self.product['price'] = -1
f = ProductForm(self.product)
self.assertFalse(f.is_valid())
self.product['price'] = 1
#### 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。
猜你喜欢
- 网上有这样一道题目:一个字符串String=“adadfdfseffserfefsefseetsdg”,找出里面出现次数最多的字母和出现的次
- 最近在做一个程序正好需要用到此方面,在网上找到过相应的程序,但用起来都非常恶,于是乎只好自己实现一个了。 首先实现两个函数用来操作光标:
- 嵌套SELECT语句也叫子查询,形如:SELECT name FROM bbc WHERE region = (SELECT region
- 学习一些你已经知道的知识也没有什么坏处,是吧?我知道这可能并不一定非常有意义,但是很多时候我们往往知其然而不知其所以然,这会使得事情出现惊人
- 0 引言上周被一则新闻震惊到了,《2454万元大奖无人认领!福彩史上第二大弃奖在广东中山产生 》,在2019年5月2日开奖的双色球中,广东中
- 继Go 1.18支持泛型后,Go 将在下个版本中支持pdqsort排序算法再次引起了开发者们的热切讨论。目前,Go仓库的最新commit中提
- jquery的ajax请求方法:$.ajax({ &nbs
- PHP SESSION 的存储Session会话存储方式PHP将session以文件的形式存储服务器的文件中,session.save_pa
- 在oracle中,函数和存储过程是经常使用到的,他们的语法中有很多相似的地方,可是也有它们的不同之处,这段时间刚学完函数与存储过程,来给自己
- 1.获取所有数据库名: SELECT Name FROM Master..SysDatabases ORDER BY Name 2.获取所有
- 如果你计划将数据库从SQL Server 2000 升级到 SQL Server 2005。你在升级之前一定会测试每样东西,并且证明应用程序
- 1.SYS用户具有DBA权限,并且拥有SYS模式,只能通过SYSDBA登陆数据库。是Oracle数据库中权限最高的帐号SYSTEM具有DBA
- 目前SQL INJECTION的攻击测试愈演愈烈,很多大型的网站和论坛都相继被注入。这些网站一般使用的多为SQL SERVER数据库,正因为
- 写 python 文件时,每个文件开头都必须注明版本和编码。每次我 touch 文件之后粘贴这两句话让我不胜其烦。由于我没有安装 pytho
- 说明:本文内容都是从Google上搜索来的,本想上http://www.alexa.com/查官方数据,访问非常慢暂且没查。使用本接口将返回
- 本文实例讲述了php简单生成随机字符串的方法。分享给大家供大家参考,具体如下:生成一组:<?php$str = "01234
- 浏览网页的时候经常会碰到一些不认识的英文单词,或者想知道一些中文单词的翻译,这时候再去找翻译软件或者翻译网站就有些麻烦了。因此我做了一个“中
- 概述PHP有着众多的内置函数,其中大多数函数都被开发者广发使用。但也有一些同样有用却被遗忘在角落,本文将介绍7个鲜为人知功能却非常酷的函数。
- 问题:m = re.findall('[0-9]*4[0-9]*', '[4]') 可以匹配到4.m = r
- 由于办公需要“每天定时推送某消息用来提醒群里面所有人”,有同事提议用企业微信自带的机器人来实现此功能。我觉得企业微信的这个工具还不错,具体使