python (4) - 錯誤處理、調試、單元測試、文檔測試

python 基礎知識整理學習

繼上一篇,python基礎知識(三)

錯誤處理、調試與測試

提前捕獲並處理代碼中可能出現的錯誤,使代碼更健壯。

錯誤處理

代碼中報錯會中斷程序,不會繼續執行。

# 打印輸出未定義的變量,報錯後不會輸出後面的數字
print(a)
print(2)

輸出錯誤:
在這裏插入圖片描述
try…except…finally
錯誤捕獲,記錄錯誤,方便查詢

try:
    print(a)
except NameError as e:
    print("變量未聲明,請檢查")
finally:
    print("繼續執行!")
print(2)

說明:

  • 剛纔的錯誤類型爲NameError,使用except語句捕獲並處理;
  • 所有的錯誤類型都繼承BaseException基類,錯誤捕獲得從子類到父類,層層向上捕獲。傳遞門
  • 錯誤會被層層傳遞,主要在於函數的調用,在哪調用錯誤會先從哪拋出,一直拋給系統,程序終止。

logging記錄
通過logging可以把錯誤記錄到錯誤日誌中,方便日後查看。也可以打印出來,程序不會被終止。

# logging 記錄日誌
import logging
try:
    print(10/0)
except ZeroDivisionError as e:
    logging.exception(e)

print(0/10)

說明:

  • logging類型記錄包括:debug/error/info/warning
  • 默認的logging.info()是不會輸出的。通過配置logging.basicConfig(level=logging.INFO)

自定義錯誤
系統內置的錯誤類型不能滿足工作,還可以自定義錯誤類型,並拋出。

# 自定義錯誤類型
class ConflictValueError(ValueError):
    pass
name = input("please input your name \n")
try:
    if name == "admin":
        raise ConflictValueError("you can't use this name")
except ConflictValueError as e:
    logging.exception(e)

print("ok")

說明:

  • 儘可能使用系統內置的錯誤類型,自定義類型繼承系統的某一個類型錯誤BaseException
  • 錯誤捕獲處理一定是有助於問題排查。
調試

功能測試,問題排查。

  • print()打印結果輸出,標記;
  • 斷言assert,斷言失敗會拋出AssertionError錯誤
    # 斷言
    assert name!="test","測試"
    
    print("大神,你好")
    
    說明:
    • 斷言拋出錯誤後,程序終止執行
    • 通過執行時加入參數python -O four.py忽略斷言。
  • logging日誌輸出記錄。
  • 調試器pdb
    python -m pdb **.py啓動調試,按步執行。
    說明:
    • l查看代碼,會顯示當前執行位置。
    • n執行下一步。
    • p 變量名來查看已執行的語句中的變量的值。
    • q結束退出。
    • c程序開始正常執行。
  • pdb.set_trace()設置斷點,程序執行到這停止。
    需要導入import pdb
單元測試

編寫一個簡單的用戶名校驗類,自定義各種錯誤類型,放置在單獨的文件four_form.py

# 自定義表單校驗錯誤類
class ConflictValueError(ValueError):
    pass
class ExistError(ValueError):
    pass
class PureNumberError(ValueError):
    pass
class LenError(ValueError):
    pass
# 表單用戶名校驗 、 不能是純數字、不能有特殊符號、必須6位以上
import re
class ValidForm():
    def __init__(self,**ot):
        self.form = ot
    def exist_name(self):
        form = self.form
        if "name" in form:
            pass
        else:
            raise ExistError("必須填寫用戶名!") 
    def conflict_name(self):
        form = self.form
        self.exist_name()
        if form["name"] in ["admin","test","login"]:
            raise ConflictValueError("你不能使用這個名字")
    def pureNumber_name(self):
        form = self.form
        self.conflict_name()
        if re.search(r'^\d+$',str(form["name"]))!=None:
            raise PureNumberError("不能是純數字!")
    def len_name(self):
        form = self.form
        self.pureNumber_name()
        if len(form["name"]) <6:
            raise LenError("用戶名不少於6個字符!")
    def isAvailable(self):
        self.len_name()
        return True
# 測試方法
# ValidForm(**{"name":"232"}).isAvailable()

編寫測試函數,單獨的文件four_test.py

import unittest

from four import ValidForm,ExistError,ConflictValueError,LenError

class TestValidForm(unittest.TestCase):
    # 在每個測試方法開始前調用 before
    def setUp(self):
        print("start")
    def test_existError(self):
        with self.assertRaises(ExistError):
            ValidForm().exist_name()

    def test_conflicError(self):
        with self.assertRaises(ConflictValueError):
            ValidForm(**{"name":"admin"}).conflict_name()
	@unittest.skip("將不會被調用")
    def test_lenError(self):
        with self.assertRaises(LenError):
            ValidForm(**{"name":"abc"}).len_name()
    # 方法調用後執行
    def tearDown(self):
        print("end")

if __name__ == '__main__':
    unittest.main()

說明:

  • 依賴模塊unittest,需導入;
  • 不同文件中,需要調用用到的模塊from ... import **
  • unittest.main()提供了在命令行運行測試腳本的接口,直接運行python four_test.py
  • setUp() 每個測試方法執行前都會去調用;tearDown()測試方法執行完後調用
  • unittest內部提供了斷言的方法,如assertRaises()/assertEqual()/assertTrue(),
  • 註解可以改變方法的表現,比如@unittest.skip() 使得這個測試方法不會被調用;
文檔測試

以註釋的方式寫測試用例,然後指定執行註釋中可執行的代碼:

import re
class ValidForm():
    '''
    valid name
    >>> ValidForm().exist_name()
    Traceback (most recent call last):
    ...
    ExistError: 必須填寫用戶名!
    >>> ValidForm(**{"name":"admin"}).conflict_name()
    Traceback (most recent call last):
    ...
    ConflictValueError: 你不能使用這個名字
    >>> ValidForm(**{"name":"admin_123"}).isAvailable()
    True
    '''
   def __init__(self,**ot):
        self.form = ot
   # 省略.....

if __name__ == "__main__":
    import doctest
    doctest.testmod()

說明:

  • >>> **必須有空格,纔會當做可執行命令執行;
  • 期望一定要和執行的結果一致;不一致時會報錯Expected: ... Got: ...
  • 當符合期望時,執行沒有任何輸出。改錯一處ValidForm(**{"name":"ad_min"}).conflict_name()
    執行結果和期待的不一致:
    在這裏插入圖片描述
  • doctest只會在命令行模式下執行。>>>標識決定了執行環境。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章