欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

對(duì)Python的Django框架中的項(xiàng)目進(jìn)行單元測(cè)試的方法

 更新時(shí)間:2016年04月11日 16:07:10   作者:心內(nèi)求法  
這篇文章主要介紹了對(duì)Python的Django框架中的項(xiàng)目進(jìn)行單元測(cè)試的方法,使用Django中的tests.py模塊可以輕松地檢測(cè)出一些常見錯(cuò)誤,需要的朋友可以參考下

 Python中的單元測(cè)試

我們先來回顧一下Python中的單元測(cè)試方法。
下面是一個(gè) Python的單元測(cè)試簡(jiǎn)單的例子:

假如我們開發(fā)一個(gè)除法的功能,有的同學(xué)可能覺得很簡(jiǎn)單,代碼是這樣的:

def division_funtion(x, y):
  return x / y

但是這樣寫究竟對(duì)還是不對(duì)呢,有些同學(xué)可以在代碼下面這樣測(cè)試:

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)

但是這樣運(yùn)行后得到的結(jié)果,自己每次都得算一下去核對(duì)一遍,很不方便,Python中有 unittest 模塊,可以很方便地進(jìn)行測(cè)試,詳情可以文章最后的鏈接,看官網(wǎng)文檔的詳細(xì)介紹。

下面是一個(gè)簡(jiǎn)單的示例:

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()


我簡(jiǎn)單地寫了三個(gè)測(cè)試示例(不一定全面,只是示范,比如沒有考慮除數(shù)是0的情況),運(yùn)行后發(fā)現(xiàn):

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)

汗!發(fā)現(xiàn)了沒,竟然兩個(gè)都失敗了,測(cè)試發(fā)現(xiàn):

4.2除以3 等于 1.4000000000000001 不等于期望值 1.4

9除以4等于2,不等于期望的 2.25

下面我們就是要修復(fù)這些問題,再次運(yùn)行測(cè)試,直到運(yùn)行不報(bào)錯(cuò)為止。

譬如根據(jù)實(shí)際情況,假設(shè)我們只需要保留到小數(shù)點(diǎn)后6位,可以這樣改:

def division_funtion(x, y):
  return round(float(x) / y, 6)

再次運(yùn)行就不報(bào)錯(cuò)了:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

 
OK

Django中的單元測(cè)試

盡早進(jìn)行單元測(cè)試(UnitTest)是比較好的做法,極端的情況甚至強(qiáng)調(diào)“測(cè)試先行”?,F(xiàn)在我們已經(jīng)有了第一個(gè)model類和Form類,是時(shí)候開始寫測(cè)試代碼了。

Django支持python的單元測(cè)試(unit test)和文本測(cè)試(doc test),我們這里主要討論單元測(cè)試的方式。這里不對(duì)單元測(cè)試的理論做過多的闡述,假設(shè)你已經(jīng)熟悉了下列概念:test suite, test case, test/test action,  test data, assert等等。

在單元測(cè)試方面,Django繼承python的unittest.TestCase實(shí)現(xiàn)了自己的django.test.TestCase,編寫測(cè)試用 例通常從這里開始。測(cè)試代碼通常位于app的tests.py文件中(也可以在models.py中編寫,但是我不建議這樣做)。在Django生成的 depotapp中,已經(jīng)包含了這個(gè)文件,并且其中包含了一個(gè)測(cè)試用例的樣例:

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)

你可以有幾種方式運(yùn)行單元測(cè)試:

  • python manage.py test:執(zhí)行所有的測(cè)試用例
  • python manage.py test app_name, 執(zhí)行該app的所有測(cè)試用例
  • python manage.py test app_name.case_name: 執(zhí)行指定的測(cè)試用例

用第三種方式執(zhí)行上面提供的樣例,結(jié)果如下:

$ 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'...

你可能會(huì)主要到,輸出信息中包括了創(chuàng)建和刪除數(shù)據(jù)庫(kù)的操作。為了避免測(cè)試數(shù)據(jù)造成的影響,測(cè)試過程會(huì)使用一個(gè)單獨(dú)的數(shù)據(jù)庫(kù),關(guān)于如何指定測(cè)試數(shù)據(jù)庫(kù) 的細(xì)節(jié),請(qǐng)查閱Django文檔。在我們的例子中,由于使用sqlite數(shù)據(jù)庫(kù),Django將默認(rèn)采用內(nèi)存數(shù)據(jù)庫(kù)來進(jìn)行測(cè)試。

下面就讓我們來編寫測(cè)試用例。在《Agile Web Development with Rails 4th》中,7.2節(jié),最終實(shí)現(xiàn)的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

對(duì)Product測(cè)試的內(nèi)容包括:

1.title,description,price,image_url不能為空;

2. price必須大于零;

3. image_url必須以jpg,png,jpg結(jié)尾,并且對(duì)大小寫不敏感;

4. titile必須唯一;

讓我們?cè)贒jango中進(jìn)行這些測(cè)試。由于ProductForm包含了模型校驗(yàn)和表單校驗(yàn)規(guī)則,使用ProductForm可以很容易的實(shí)現(xiàn)上述測(cè)試:

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結(jié)尾,并且對(duì)大小寫不敏感;
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'

然后運(yùn)行 python manage.py test depotapp.ProductTest。如同預(yù)想的那樣,測(cè)試沒有通過:

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'...

因?yàn)槲覀冎安]有考慮到image_url的圖片擴(kuò)展名可能會(huì)大寫。修改ProductForm的相關(guān)部分如下:

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

然后再運(yùn)行測(cè)試:

$ 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'...

測(cè)試通過,并且通過單元測(cè)試,我們發(fā)現(xiàn)并解決了一個(gè)bug。

 

相關(guān)文章

最新評(píng)論