為什么要測試?
Web程序開發過程一般包括以下幾個階段:[需求分析,設計階段,實現階段,測試階段]。其中測試階段通過人工或自動來運行測試某個系統的功能。目的是檢驗其是否滿足需求,並得出特定的結果,以達到弄清楚預期結果和實際結果之間的差別的最終目的。
測試的分類:
測試從軟件開發過程可以分為:
- 單元測試
- 對單獨的代碼塊(例如函數)分別進行測試,以保證它們的正確性
- 集成測試
- 對大量的程序單元的協同工作情況做測試
- 系統測試
- 同時對整個系統的正確性進行檢查,而不是針對獨立的片段
在眾多的測試中,與程序開發人員最密切的就是單元測試,因為單元測試是由開發人員進行的,而其他測試都由專業的測試人員來完成。所以我們主要學習單元測試。
什么是單元測試?
程序開發過程中,寫代碼是為了實現需求。當我們的代碼通過了編譯,只是說明它的語法正確,功能能否實現則不能保證。 因此,當我們的某些功能代碼完成后,為了檢驗其是否滿足程序的需求。可以通過編寫測試代碼,模擬程序運行的過程,檢驗功能代碼是否符合預期。
單元測試就是開發者編寫一小段代碼,檢驗目標代碼的功能是否符合預期。通常情況下,單元測試主要面向一些功能單一的模塊進行。
舉個例子:一部手機有許多零部件組成,在正式組裝一部手機前,手機內部的各個零部件,CPU、內存、電池、攝像頭等,都要進行測試,這就是單元測試。
在Web開發過程中,單元測試實際上就是一些“斷言”(assert)代碼。
斷言就是判斷一個函數或對象的一個方法所產生的結果是否符合你期望的那個結果。 python中assert斷言是聲明布爾值為真的判定,如果表達式為假會發生異常。單元測試中,一般使用assert來斷言結果。
斷言方法的使用:

斷言語句類似於:
if not expression:
raise AssertionError
AssertionError
常用的斷言方法:
assertEqual 如果兩個值相等,則pass assertNotEqual 如果兩個值不相等,則pass assertTrue 判斷bool值為True,則pass assertFalse 判斷bool值為False,則pass assertIsNone 不存在,則pass assertIsNotNone 存在,則pass
如何測試?
簡單的測試用例:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,
def fibo(x):
if x == 0:
resp = 0
elif x == 1:
resp = 1
else:
return fibo(x-1) + fibo(x-2)
return resp
assert fibo(5) == 5

單元測試的基本寫法:
首先,定義一個類,繼承自unittest.TestCase
import unittest
class TestClass(unitest.TestCase):
pass
其次,在測試類中,定義兩個測試方法
import unittest
class TestClass(unittest.TestCase):
#該方法會首先執行,方法名為固定寫法
def setUp(self):
pass
#該方法會在測試代碼執行完后執行,方法名為固定寫法
def tearDown(self):
pass
最后,在測試類中,編寫測試代碼
import unittest
class TestClass(unittest.TestCase):
#該方法會首先執行,相當於做測試前的准備工作
def setUp(self):
pass
#該方法會在測試代碼執行完后執行,相當於做測試后的掃尾工作
def tearDown(self):
pass
#測試代碼
def test_app_exists(self):
pass
登錄測試
- 被測試的代碼邏輯
@app.route('/login', methods=['POST']) def login(): username = request.form.get('username') password = request.form.get('password') # 判斷參數是否為空 if not all([username, password]): result = { "errcode": -2, "errmsg": "params error" } return jsonify(result) # a = 1 / 0 # 如果賬號密碼正確 # 判斷賬號密碼是否正確 if username == 'skylark' and password == 'python': result = { "errcode": 0, "errmsg": "success" } return jsonify(result) else: result = { "errcode": -1, "errmsg": "wrong username or password" } return jsonify(result)
- 單元測試代碼
import json
import unittest
from demo1_login import app
class LoginTest(unittest.TestCase):
"""為登錄邏輯編寫測試案例"""
def setUp(self):
app.testing = True
self.client = app.test_client()
def test_empty_username_password(self):
"""測試用戶名與密碼為空的情況[當參數不全的話,返回errcode=-2]"""
response = app.test_client().post('/login', data={})
json_data = response.data
json_dict = json.loads(json_data)
self.assertIn('errcode', json_dict, '數據格式返回錯誤')
self.assertEqual(json_dict['errcode'], -2, '狀態碼返回錯誤')
# TODO 測試用戶名為空的情況
# TODO 測試密碼為空的情況
def test_error_username_password(self):
"""測試用戶名和密碼錯誤的情況[當登錄名和密碼錯誤的時候,返回 errcode = -1]"""
response = app.test_client().post('/login', data={"username": "aaaaa", "password": "12343"})
json_data = response.data
json_dict = json.loads(json_data)
self.assertIn('errcode', json_dict, '數據格式返回錯誤')
self.assertEqual(json_dict['errcode'], -1, '狀態碼返回錯誤')
# TODO 測試用戶名錯誤的情況
# TODO 測試密碼錯誤的情況
if __name__ == '__main__':
unittest.main()
數據庫測試:
#coding=utf-8
import unittest
from author_book import *
#自定義測試類,setUp方法和tearDown方法會分別在測試前后執行。以test_開頭的函數就是具體的測試代碼。
class DatabaseTestCase(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@localhost/test0'
self.app = app
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
#測試代碼
def test_append_data(self):
au = Author(name='skylark')
bk = Book(info='python')
db.session.add_all([au,bk])
db.session.commit()
author = Author.query.filter_by(name='skylark').first()
book = Book.query.filter_by(info='python').first()
#斷言數據存在
self.assertIsNotNone(author)
self.assertIsNotNone(book)
