RobotFramework之Python呼叫RF關鍵字
背景
這個需求說實話是有那麼些奇葩,因為正常的框架不會用到這個需求,要麼就純Python
來實現,要麼就純RobotFramework
來實現,用RobotFramework
的時候是可以正常呼叫Python
的,但是反過來就有點蛋疼了。
我們現在的框架就是基於RobotFramework
實現的,但是由於RobotFramework
在某些地方的擴充套件性太低,某些時候用Python
來實現更為高效,不過現有的RobotFramework
工程已經有非常多的沉澱了,要是把這部分全部重寫,工作量太大了,因此就有了這麼個奇葩的需求。在網上搜索了大半天后,終於還是放棄了,只能自己去看原始碼來實現了。
簡單粗暴的實現
有一種非常簡單粗暴的實現方式,用RobotFramework
寫一個測試用例,然後這個用例呼叫對應的關鍵字,再用Python
來執行這條用例,就實現了呼叫關鍵字的功能,但是這樣有兩個弊端。
- 每次呼叫關鍵字都要寫測試用例,非常麻煩
- 如果要獲取返回值,就很蛋疼了
更為優雅的實現
整個部分實現起來其實不難,只不過RobotFramework
的原始碼沒有什麼人去解讀,也沒有很好的翻譯文件,其實在之前的解析文章中用到的robot.api
中就已經涉及到這個點了,實現程式碼如下:
class RobotAbout(object):
"""
呼叫RobotFramework的方法
eg:
ra = RobotAbout.RobotAbout()
ra.exec_keywords('xxx.robot', ['獲取隨機數', '獲取計數'])
"""
def exec_keyword(self, path_to_file, keyword, arg=None):
"""
執行RobotFramework的關鍵字
:param path_to_file: String,RobotFramework檔案的路徑,一般是執行檔案的相對路徑,絕對路徑也可以
:param keyword: String,執行的關鍵字
:param arg: List[String], 執行關鍵字的引數,如果沒有則不傳
:return: None, 沒有返回則表示執行通過,可以檢視執行檔案目錄下的log.html來定位問題
"""
suite = TestSuite('Test')
suite.resource.imports.resource(path_to_file)
test = suite.tests.create('TEST', tags=['smoke'])
if arg:
if isinstance(arg, list):
test.keywords.create(keyword.decode('utf-8'), args=arg)
else:
raise TypeError('args must be list')
else:
test.keywords.create(keyword.decode('utf-8'))
result = suite.run(critical='smoke')
if result.suite.status == 'PASS':
pass
else:
ResultWriter('test.xml').write_results()
給我靈感的頁面就在官方的說明文件上,這裡有一段非常有意思的說明:
這裡提供了兩種方式執行RobotFramework
,一種是用TestSuiteBuilder
,這種其實跟我們之前的方法沒啥區別,都是執行一個測試用例,第二種方法則是用Python
來寫一個RobotFramework
的測試用例,這種思路可以通過抽象成一個類,就可以達到優雅的執行RobotFramework
的關鍵字了。
需要注意,官方文件寫的是suite.resource.imports.library
,而RobotFramework
的檔案引用都是要用Resource
來引用的,不能原樣照搬。所以這裡我們要去看它的原始碼來實現:
robot.running.model.py
class ResourceFile(object):
def __init__(self, doc='', source=None):
self.doc = doc
self.source = source
self.imports = []
self.keywords = []
self.variables = []
@setter
def imports(self, imports):
return model.Imports(self.source, imports)
@setter
def keywords(self, keywords):
return model.ItemList(UserKeyword, items=keywords)
@setter
def variables(self, variables):
return model.ItemList(Variable, {'source': self.source}, items=variables)
從這裡我們能看到,imports
是由一個裝飾器@setter
來處理的,那麼只要跟進它的方法就能看到:
robot.model.imports.py
class Imports(ItemList):
def __init__(self, source, imports=None):
ItemList.__init__(self, Import, {'source': source}, items=imports)
def library(self, name, args=(), alias=None):
self.create('Library', name, args, alias)
def resource(self, path):
self.create('Resource', path)
def variables(self, path, args=()):
self.create('Variables', path, args)
這裡就告訴了我們,import
這個列表中可以放library
,resource
和variables
因此在這裡我們只要把原始碼改成suite.resource.imports.resource(path_to_file)
就可以正常的引用RobotFramework
的關鍵字了。
其他注意點
在除錯的過程中我發現一個問題,如果關鍵字用中文編寫,那麼必須要把呼叫的關鍵字編碼成utf-8
,否則會報錯找不到關鍵字。就像上面的程式碼這樣處理:
test.keywords.create(keyword.decode('utf-8'), args=arg)
或者如果你不在方法中處理,也可以這樣傳入引數:
ra.exec_keyword('../xxx/xxx.robot', u'獲取隨機數')