python自動化測試之異常及日誌

Secret608發表於2018-10-31

  為了保持自動化測試用例的健壯性,異常的捕獲及處理,日誌的記錄對掌握自動化測試執行情況尤為重要,這裡便詳細的介紹下在自動化測試中使用到的異常及日誌,並介紹其詳細的用法。

  一、日誌

    列印日誌是很多程式的重要需求,良好的日誌輸出可以幫我們更方便的檢測程式執行狀態。Python標準庫提供了logging模組,切記Logger從來不直接例項化,其好處不言而喻,接下來慢慢講解。 

  1.  logging之控制檯輸出
    import logging
    #設定日誌,包括filename、level、format、filemode、stream,其中format屬性極其豐富,詳情可檢視API文件,這裡只做簡要介紹
    logging.basicConfig(level = logging.INFO,format = `%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    logger = logging.getLogger(__name__)
    #訊息級別,五級
    logger.debug("芹澤多摩雄") 
    logger.info("真")
    logger.warning("男")
    logger.error("")
    logger.critical("")
  2.   logging之檔案輸出

    import logging
    logger = logging.getLogger(__name__)
    logger.setLevel(level = logging.INFO)
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter(`%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.debug("芹澤多摩雄") 
    logger.info("")
    logger.warning("")
    logger.error("")
    logger.critical("")

    細心的盆友就會知道,其實以上兩種方式其實是互通的,即實現日誌記錄輸出的兩種渠道,就好比一根水管,其中有一個分支通向檔案,一個分支通向控制檯,具體用那種,看具體場景。

  3. 將日誌同時輸出到控制檯和檔案中
    import logging
    logger = logging.getLogger(__name__)
    logger.setLevel(level = logging.INFO)
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter(`%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    handler.setFormatter(formatter)
     
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
     
    logger.addHandler(handler)
    logger.addHandler(console)
    
    logger.debug("芹澤多摩雄") 
    logger.info("")
    logger.warning("")
    logger.error("")
    logger.critical("")

    細心的盆友又可以發現,可以發現,logging有一個日誌處理的主物件,其他處理方式都是通過addHandler新增進去,這裡採用logging.StreamHandler實現日誌輸出到流(控制檯),也可以用FileHandler實現日誌輸出到檔案

  4. 日誌回滾
    import logging
    from logging.handlers import RotatingFileHandler
    logger = logging.getLogger(__name__)
    logger.setLevel(level = logging.INFO)
    #定義一個RotatingFileHandler,最多備份3個日誌檔案,每個日誌檔案最大1K
    rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3)
    rHandler.setLevel(logging.INFO)
    formatter = logging.Formatter(`%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    rHandler.setFormatter(formatter)
     
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(formatter)
     
    logger.addHandler(rHandler)
    logger.addHandler(console)
    logger.debug("芹澤多摩雄") 
    logger.info("")
    logger.warning("")
    logger.error("")
    logger.critical("")
  5. 多模組使用
    #主模組
    import logging
    import subModule
    logger = logging.getLogger("mainModule")
    logger.setLevel(level = logging.INFO)
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter(`%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    handler.setFormatter(formatter)
     
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(formatter)
     
    logger.addHandler(handler)
    logger.addHandler(console)
    
    #子模組
    import logging
     
    module_logger = logging.getLogger("mainModule.sub")
    class SubModuleClass(object):
        def __init__(self):
            self.logger = logging.getLogger("mainModule.sub.module")

    細心的盆友會再次發現其實對logger的命名很重要,首先在主模組定義了logger`mainModule`,並對它進行了配置,子模組可以共享父logger的定義和配置,所謂的父子logger是通過命名來識別,任意以`mainModule`開頭的logger都是它的子logger,例如`mainModule.sub`

  6. 事實上,縱使有繼承配置或者自定義的配置日誌功能,但實際中的大專案中還是略麻煩的,這裡主要用到JSON或者yaml進行配置封裝,這樣載入該檔案即可載入日誌的配置,下回分解具體操作,最後來一發例項。
    # -*- coding: utf-8 -*-
    __author__ = `Secret608`
    
    import logging
    import time
    import os
    import re
    
    
    class Log(object):
    
    
        def __init__(self, loggerName):
                ```
                進行日誌初始化,包括儲存路徑、名稱、級別、呼叫檔案等
                ```
                #基本屬性
                self.logger = logging.getLogger(loggerName)
                self.logger.setLevel(logging.WARNING)
    
                #特有屬性(檔案地址+日誌記錄格式)
                rq = time.strftime(`%Y%m%d_%H%M%S`, time.localtime(time.time()))
                log_path = os.path.join(os.path.dirname(os.getcwd()), `logs`)
                log_title = os.path.join(log_path, loggerName + `_`+ rq) + ".log"
                formatter = logging.Formatter(`%(asctime)s - %(name)s - %(levelname)s - %(message)s`)
    
                log = logging.FileHandler(log_title)
                log.setFormatter(formatter)
    
                #加到基本屬性中,得到一個完整的初始化物件
                self.logger.addHandler(log)
    
    
        def getLog(self):
    
            return self.logger
    
        def delLog(self, fileName):
    
            log_path = os.path.join(os.path.dirname(os.getcwd()), `logs`)
            regexp = re.compile(`^`+fileName+`s.*`)
            filelist = os.listdir(log_path)
            try:
                [os.remove(os.path.join(log_path, i)) for i in filelist if regexp.match(i) == None]
            except WindowsError:
                pass
            else:
                return "ok"
    
    
    if __name__ == "__main__":
        a = Log("hah")
        a.delLog("hah")

     

 

  二、異常

    • 異常型別
      • 內建異常:Python的異常處理能力是很強大的,它有很多內建異常,可向使用者準確反饋出錯資訊。在Python中,異常也是物件,可對它進行操作。BaseException是所有內建異常的基類,但使用者定義的類並不直接繼承BaseException,所有的異常類都是從Exception繼承,且都在exceptions模組中定義。
      • 自定義異常:可以通過建立一個新的異常類擁有自己的異常,異常應該是通過直接或間接的方式繼承自Exception類。比如建立了一個MyError類,基類為Exception,用於在異常觸發時輸出更多的資訊。
  • 異常捕獲
    • 發生異常時,我們就需要對異常進行捕獲,然後進行相應的處理。python的異常捕獲常用try…except…結構,把可能發生錯誤的語句放在try模組裡,用except來處理異常,每一個try,都必須至少對應一個except。此外,與python異常相關的關鍵字主要有:try/except、pass、as(定義異常例項)、else、finally、raise。
    • 捕獲所有異常:
    •  

      # -*- coding: utf-8 -*-
      
      #異常處理的語法:
      try#執行可能出現異常的語句
      except `異常名字`#出現異常執行的語句
      else#執行沒有出現異常的語句
      finally:
          #異常與否都執行的語句
      
      #demo
      try:
          a = 0
          b = 1
          c = b/a
          print(c)
      except ZeroDivisionError:
          print("分母不能為0")
      except NameError:
          print("名稱錯誤")
      except (ZeroDivisionError, NameError):
          print("你的分母等於0或者變數名不存在")
      except Exception as e:
          print("你的變數名或者分母值確實沒錯,但是出現了其他的錯誤,詳見%s" %e)
          d = 1   
      finally:
          print("聽說沒有錯誤?不能忍,反正我要讓你難受!")
          if d == 1:
              raise ValueError("你有其它錯誤")
          elseraise FuckError("開不開心?")
          #咦? FuckError是啥?貌似沒有定義啊,這裡定義一波,屆時位置移動到前面去
          class FuckError(Exception):
              def __int__(self,*args,**keargs):
                  super(FuckError,self).__int__(*args,**keargs)#python2
                  self.args = args
                  print(args)   

       

    • 更多的異常可參看API(https://docs.python.org/3/library/exceptions.html#base-classes

 

相關文章