Airtest通過代碼生成報告——simple_report、LogToHtml詳解


上期回顧: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分獨立的測試報告:

 

 

 

 

---------------------------------------------------------------------------------

關注微信公眾號即可在手機上查閱,並可接收更多測試分享~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM