Robot Framework: 擴充現有的 Test Library

官方的 User Guide 提到,BuiltIn Library 是擴充現有 test library 最好的方法。下面以擴充 Screenshot Library 為例:

examples/extkw/test.txt

| *Setting* | *Value*
| Library | Screenshot
| Library | MyScreenshotLibrary

| *Test Case* | *Action* | *Argument*
| Test | Take Snapshot | desktop
Caution 雖然沒有直接用到 Screenshot Library,但如果沒有明確引進來的話,執行期會出現 “No library with name ‘Screenshot’ found." 的錯誤,建議在 test library 的文件上額外做說明。

examples/extkw/MyScreenshotLibrary

from robot.libraries.BuiltIn import BuiltIn

class MyScreenshotLibrary:

  _builtin = BuiltIn()

  @property
  def _screenshot_lib(self):
    return self._builtin.get_library_instance('Screenshot') 1

  def take_snapshot(self, name, width='800px'):
      img = self._screenshot_lib.take_screenshot(name, width) 2
      print '*WARN* snapshot:', img
1 透過 BuiltIn instance 取得其他 library 的 active instance,跟 test library 的 scope 無關。
2 等同在 test data 裡 Screenshot.Take Screenshot 的用法。

執行結果:

$ pybot -d /tmp test.txt
==============================================================================
Test
==============================================================================
[ WARN ] snapshot: /tmp/desktop_1.jpg
Test                                                                  | PASS |
Tip

BuiltIn Library 是 global scope,我們手動生成的那個 BuiltIn instance 當然跟 runtime 系統唯一的那個 BuiltIn Library instance 不同,但我們卻可以透過它去取得其他 library instance?

那是因為 test library instance 並不是記錄在 BuiltIn Library instance 裡:

libraries/BuiltIn.py

from robot.running import Keyword, NAMESPACES, RUN_KW_REGISTER

class _Misc:

    def get_library_instance(self, name):
        try:
            return self._namespace.get_library_instance(name)
        except DataError, err:
            raise RuntimeError(unicode(err))

class BuiltIn(_Verify, _Converter, _Variables, _RunKeyword, _Misc):
    ROBOT_LIBRARY_SCOPE = 'GLOBAL'

    @property
    def _namespace(self):
        return NAMESPACES.current

running/__init__.py

class _Namespaces:

# Hook to namespaces
NAMESPACES = _Namespaces() 1
1 原來 NAMESPACES 是個 singleton。

或許 BuiltIn.get_library_instance() 一開始就被設計成 static/class method 就不會有使用上的疑慮了。

如果這是改變不了的事實,建議將 BuiltIn instance 快取起來,這樣取用 test case 或 test suite scope 的 library instance 時,就不用一直生成全新的 BuiltIn instance。

下面示範擴充 Selenium Library 的做法:

MySeleniumLibrary.py

class MySeleniumLibrary:

    _builtin = BuiltIn()
    _selenium_lib = None

    @property
    def _screenshot_lib(self):
        return self._builtin.get_library_instance('Screenshot') # test suite scope

    @property
    def _selenium(self):
        if self._selenium_lib is None:
            self._selenium_lib = self._builtin.get_library_instance('SeleniumLibrary')
        return self._selenium_lib._selenium

    def login(self, username, password, language=None, service_hint=None):
        self._selenium.open(...)
        self._screenshot_lib.take_screenshot()
Warning

get_library_instance() 不能在 __init() 裡呼叫,否則 RIDE 和真正跑測試時,會分別出現下面的錯誤:(事實上任何初始化的工作都不應該在 __init__() 裡做)

AttributeError: 'NoneType' object has no attribute 'get_library_instance'
[ ERROR ] Invalid syntax in file 'xxx.txt' in table 'Settings': Creating an instance of the test library 'MyLibrary' with no arguments failed: No library with name 'SeleniumLibrary' found.

顯然我們不能假設 Robot Framework 載入 test libraries 的順序…

Note

參考資料

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s