上期回顧:Airtest生成報告命令行airtest report詳解
以下基於
python3.8;airtestIDE1.2.11;airtest1.2.2;pocoui1.0.83
上期我們講了在命令行生成報告,這次我們看下怎么通過腳本直接在代碼中運行生成報告。分別是LogToHtml類和simple_report()函數。
LogToHtml
上期我們講airtest report時,源碼里已經看到了,最終生成報告,就是實例化LogToHtml類,並調用了里面的report()方法,那這次我們詳細看下其源碼。
先看下LogToHtml類的初始化
1# 文件位置:your_python_path/site-packages/airtest/report/report.py
2class LogToHtml(object):
3 """Convert log to html display """
4 def __init__(self, script_root, log_root="", static_root="", export_dir=None, script_name="", logfile=None, lang="en", plugins=None):
5 self.log = []
6 self.script_root = script_root
7 self.script_name = script_name
8 if not self.script_name or os.path.isfile(self.script_root):
9 self.script_root, self.script_name = script_dir_name(self.script_root)
10 self.log_root = log_root or ST.LOG_DIR or os.path.join(".", DEFAULT_LOG_DIR)
11 self.static_root = static_root or STATIC_DIR
12 self.test_result = True
13 self.run_start = None
14 self.run_end = None
15 self.export_dir = export_dir
16 self.logfile = logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE)
17 self.lang = lang
18 self.init_plugin_modules(plugins)
參數說明:
-
script_root:腳本所在文件夾
-
log_root:log.txt所在文件夾
-
static_root:部署靜態資源的服務器路徑
-
export_dir:導出報告文件夾
-
script_name:腳本名稱
-
logfile:log.txt的路徑
-
lang:報告的語言,中文:zh;英文:en(默認)
-
plugins:插件,使用了poco框架,需要填寫
--plugin poco.utils.airtest.report
。如果用到了airtest-selenium,需要填寫`--plugin airtest_selenium.report
源碼解析:
只說下重要的
第8行:如果沒有指定腳本名稱,則使用script_dir_name()通過前面給的腳本所在文件夾自行獲取
第10行:指定了log.txt所在文件夾則直接賦值,否則使用全局變量ST.LOG_DIR給的路徑(就是說你可以在代碼最開始通過全局變量指定日志路徑)。如果都沒有,則取當前目錄下的log文件夾。
第16行:如果指定了log.txt全路徑則直接賦值,否則使用全局變量ST.LOG_FILE給的路徑(默認是log.txt,你可以在代碼最開始通過全局變量指定日志全路徑)
LogToHtml類里有各種方法去獲取生成報告所需的各種數據,這里我們只看一個我們直接接觸到的:
1 def _translate_desc(self, step, code):
2 """ 函數描述 """
3 if step['tag'] != "function":
4 return None
5 name = step['data']['name']
6 res = step['data'].get('ret')
7 args = {i["key"]: i["value"] for i in code["args"]}
8
9 desc = {
10 "snapshot": lambda: u"Screenshot description: %s" % args.get("msg"),
11 "touch": lambda: u"Touch %s" % ("target image" if isinstance(args['v'], dict) else "coordinates %s" % args['v']),
12 "swipe": u"Swipe on screen",
13 "wait": u"Wait for target image to appear",
14 "exists": lambda: u"Image %s exists" % ("" if res else "not"),
15 "text": lambda: u"Input text:%s" % args.get('text'),
16 "keyevent": lambda: u"Click [%s] button" % args.get('keyname'),
17 "sleep": lambda: u"Wait for %s seconds" % args.get('secs'),
18 "assert_exists": u"Assert target image exists",
19 "assert_not_exists": u"Assert target image does not exists",
20 }
21
22 # todo: 最好用js里的多語言實現
23 desc_zh = {
24 "snapshot": lambda: u"截圖描述: %s" % args.get("msg"),
25 "touch": lambda: u"點擊 %s" % (u"目標圖片" if isinstance(args['v'], dict) else u"屏幕坐標 %s" % args['v']),
26 "swipe": u"滑動操作",
27 "wait": u"等待目標圖片出現",
28 "exists": lambda: u"圖片%s存在" % ("" if res else u"不"),
29 "text": lambda: u"輸入文字:%s" % args.get('text'),
30 "keyevent": lambda: u"點擊[%s]按鍵" % args.get('keyname'),
31 "sleep": lambda: u"等待%s秒" % args.get('secs'),
32 "assert_exists": u"斷言目標圖片存在",
33 "assert_not_exists": u"斷言目標圖片不存在",
34 }
35
36 if self.lang == "zh":
37 desc = desc_zh
38
39 ret = desc.get(name)
40 if callable(ret):
41 ret = ret()
42 return ret
看到了嗎,我們看的報告中的步驟描述,就是在這里定義的,如果你想修改,可以直接修改這里。
實例化LogToHtml類之后,就可以使用類中的方法report()生成報告了
1 def report(self, template_name=HTML_TPL, output_file=HTML_FILE, record_list=None):
2 """
3 Generate the report page, you can add custom data and overload it if needed
4
5 :param template_name: default is HTML_TPL
6 :param output_file: The file name or full path of the output file, default HTML_FILE
7 :param record_list: List of screen recording files
8 :return:
9 """
10 if not self.script_name:
11 path, self.script_name = script_dir_name(self.script_root)
12
13 if self.export_dir:
14 self.script_root, self.log_root = self._make_export_dir()
15 # output_file可傳入文件名,或絕對路徑
16 output_file = output_file if output_file and os.path.isabs(output_file) \
17 else os.path.join(self.script_root, output_file or HTML_FILE)
18 if not self.static_root.startswith("http"):
19 self.static_root = "static/"
20
21 if not record_list:
22 record_list = [f for f in os.listdir(self.log_root) if f.endswith(".mp4")]
23 data = self.report_data(output_file=output_file, record_list=record_list)
24 return self._render(template_name, output_file, **data)
參數說明:
-
template_name:就是/your_python_path/site-packages/airtest/report/log_template.html,感興趣可以打開看看以及看下源碼。另外,你也可以傳入自己的模板。
-
output_file:日志全路徑,默認為log.html
-
record_list:錄像文件列表
源碼解析:
前面分別獲取腳本路徑和名稱、導出文件和路徑、錄像列表。
之后調用report_data()方法,該方法就是獲取所有報告信息的,里面調用了LogToHtml類中的很多方法去獲取生成報告所需的各種各樣的數據,感興趣的可以自己看看。
最后調用並返回_render(),該方法其實就是用jinja2配合HTML模板生成報告(Jinja2是Python下的一個模板引擎,用來生成HTML網頁)
可以看下_render()的實現:
1 @staticmethod
2 def _render(template_name, output_file=None, **template_vars):
3 """ 用jinja2渲染html"""
4 env = jinja2.Environment(
5 loader=jinja2.FileSystemLoader(STATIC_DIR),
6 extensions=(),
7 autoescape=True
8 )
9 env.filters['nl2br'] = nl2br
10 env.filters['datetime'] = timefmt
11 template = env.get_template(template_name)
12 html = template.render(**template_vars)
13
14 if output_file:
15 with io.open(output_file, 'w', encoding="utf-8") as f:
16 f.write(html)
17 LOGGING.info(output_file)
18
19 return html
實例演示
1__author__ = '公眾號:測試工程師小站'
2
3from airtest.core.api import *
4from airtest.report.report import LogToHtml
5
6auto_setup(__file__,logdir=True)
7
8touch([500, 500]) # 此一句代表整個腳本
9
10r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=r'D:\code\Airtest\report\test1', lang='zh')
11r.report() # 用法1:生成報告就在導出文件夾下,名字為log.html
12
13# 用法2:所有的資源文件在導出文件夾,但HTML文件在report文件夾,且叫custom_report.html
14# 個人不建議html文件與資源文件分離,放在一起不好管理嗎?
15# r.report(output_file=r'D:\code\Airtest\report\custom_report.html')
將用例代碼和生成報告代碼寫一起,有個缺點就是用例失敗后,后面的代碼就不會執行了,可以加try,改造后的代碼
1__author__ = '公眾號:測試工程師小站'
2
3from airtest.core.api import *
4from airtest.report.report import LogToHtml
5
6auto_setup(__file__,logdir=True)
7try:
8 touch([500, 500]) # 此一句代表整個腳本
9except:
10 print('發生異常,在這寫異常處理語句,或是重新運行一遍腳本')
11finally: # 不管腳本成功與否,都會執行finally塊中的語句
12 r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=r'D:\code\Airtest\report\test1', lang='zh')
13 r.report()
現在還有個問題,就是用代碼生成報告,所有東西我們都寫死了,這樣每次報告都會相互覆蓋,如果想保留每次報告,我們只需要將報告導出路徑自定義一下,比如每次都導到一個新的文件夾,名字為:用例名_日期。
1__author__ = '公眾號:測試工程師小站'
2
3import time, os
4from airtest.core.api import *
5from airtest.report.report import LogToHtml
6
7auto_setup(__file__,logdir=True)
8try:
9 touch([500, 500]) # 此一句代表整個腳本
10except:
11 print('發生異常,在這寫異常處理語句,或是重新運行一遍腳本')
12finally: # 不管腳本成功與否,都會執行finally塊中的語句
13 casename = os.path.basename(__file__) # 用例名就取文件名,或者你也可以通過其他方式定義
14 casename = casename.split('.')[0]
15 dt = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
16 dirname = casename + dt
17 r = LogToHtml(script_root=r'D:\code\Airtest\test1.air', log_root=r'D:\code\Airtest\test1.air\log', export_dir=dirname, lang='zh')
18 r.report()
simple_report()
simple_report()與LogToHtml類在同一個文件中,是一個獨立的函數。
1# 文件位置:your_python_path/site-packages/airtest/report/report.py
2def simple_report(filepath, logpath=True, logfile=None, output=HTML_FILE):
3 path, name = script_dir_name(filepath)
4 if logpath is True:
5 logpath = os.path.join(path, getattr(ST, "LOG_DIR", DEFAULT_LOG_DIR))
6 rpt = LogToHtml(path, logpath, logfile=logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE), script_name=name)
7 rpt.report(HTML_TPL, output_file=output)
參數說明:
-
filepath:對應的是LogToHtml初始化時的script_root參數,腳本文件的全路徑,可以直接傳入變量__file__
-
logpath:對應的是LogToHtml初始化時的log_root參數。布爾值,為True時,使用全局變量ST.LOG_DIR給的路徑(就是說你可以在代碼最開始通過全局變量指定日志路徑),如果都沒有,則取當前目錄下的log文件夾
-
logfile:對應的是LogToHtml初始化時的logfile參數,log.txt的文件路徑
-
output:對應的是report()方法中的output_file參數,報告導出全路徑,必須以.html結尾
源碼解析:
第1行獲取腳本路徑;
第2、3行獲取日志路徑;
第5行實例化LogToHtml;
第6行調用LogToHtml類的report()方法生成報告。
代碼邏輯很簡單,實際上調用的還是LogToHtml。但是使用simple_report()無法導出報告,只能在本地查看。
實例演示
我個人覺得simple_report()碼如其名,就是給大家一個最簡單的生成報告的方法。所以我個人覺得使用時就應該全用默認值參數:
1__author__ = '公眾號:測試工程師小站'
2
3from airtest.core.api import *
4from airtest.report.report import simple_report
5
6auto_setup(__file__,logdir=True)
7
8touch([500, 500]) # 此一句代表整個腳本
9 # 其他參數全用默認的,即在當前腳本路徑下生成名為log.html的報告
10simple_report(__file__)
以上就是LogToHtml類和simple_report()函數的使用了。但如果你有意將自動化做的正規且容入項目流程中,那必然要涉及CI,比如用Jenkins自動執行。如果要自動執行,那就還是得用命令行調用的方法(airtest run、airtest report)。
所以本篇內容不建議你實用,僅做為了解生成報告的內在邏輯學習,實用的還是看看前2期的airtest run、airtest report。
下面再介紹官網提供的一個多次運行同一腳本,並每次生成報告的例子:
利用 while循環,重復執行3次完整的腳本內容,並依次生成 log1.html、log2.html 和 log3.html:
a = 1
while a < 4:
# 執行完整的腳本內容
pass
# 生成報告
from airtest.report.report import simple_report
simple_report(__file__,logpath=True,output="log"+str(a)+".html")
a = a + 1
最終log查看窗打印的部分結果如下:
也生成了3分獨立的測試報告:
---------------------------------------------------------------------------------
關注微信公眾號即可在手機上查閱,並可接收更多測試分享~