自動化測試 RobotFramework自定義靜態測試類庫總結

授客發表於2024-09-18

實踐環境

win11 家庭中文版

Python 3.9.13

robotframework6.1.1

說明:為了方便的使用robot命令,安裝好robotframwork後,修改系統環境,新增robot.exe(PYTHON_HOME/Scripts/robot.exe)所在路徑到系統環境變數path

安裝參考連線:https://github.com/robotframework/robotframework/blob/master/INSTALL.rst

docutils-0.21.2-py3-none-any.whl

pip install docutils==0.21.2

robotframework-ride 2.0.8.1

建立測試類庫

建立測試庫類或者模組

可透過Python模組或者類實現測試類庫

類庫名稱

當某個庫被匯入時庫時使用的測試庫的名稱與實現它的模組或類的名稱相同。例如,如果您有一個Python模組MyLibrary(即檔案MyLibrary.py),它將建立一個名為MyLibrary的庫。

Python類總是在模組內部。如果實現庫的類的名稱與模組的名稱相同,則Robot Framework允許在匯入庫時省略類名。例如,MyLib.py檔案中的類MyLib可以用作名為MyLib的庫。這也適用於子模組,例如,如果parent.MyLib模組具有類MyLib,也可以僅使用parent.MyLib匯入。如果模組名稱和類名不同,則必須同時使用模組和類名,如mymodule.MyLibrary 或者parent.submodule.MyLib

建議:

如果類庫名稱比較長,推薦給類庫取個簡單的別名。

為類庫設定別名

如果多個關鍵字具有相同的名稱,則必須關鍵字名稱前面加上庫名稱作字首。庫名稱通常來自實現它的模組或類名,但在某些情況下需要更改它:

  1. 需要使用不同的引數多次匯入同一個庫。
  2. 庫名稱太長,不方便使用。
  3. 希望使用變數在不同的環境中匯入不同的庫,但使用相同的名稱引用它們。
  4. 該庫的名稱具有誤導性,或者在其他方面很差。在這種情況下,更改實際名稱當然是更好的解決方案。

指定新名稱的基本語法是在庫名稱後面加上文字AS(區分大小寫),然後再加上新名稱。指定的名稱顯示在日誌中,當使用關鍵字的全名(LibraryName.Keyword name)時,必須在測試資料中使用。

*** Settings ***
Library    packagename.TestLib    AS    TestLib
Library    ${LIBRARY}    AS    MyName

使用不同引數多次匯入相同類庫

*** Settings ***
Library    SomeLibrary    localhost        1234    AS    LocalLib
Library    SomeLibrary    server.domain    8080    AS    RemoteLib

*** Test Cases ***
Example
    LocalLib.Some Keyword     some arg       second arg
    RemoteLib.Some Keyword    another arg    whatever
    LocalLib.Another Keyword

為類庫提供引數

所有作為類實現的測試類庫,都可以接收引數。在Setting部分,類庫名稱後面指定這些引數,當Robot Framework建立匯入庫的例項時,會將這些引數傳遞給其建構函式。如果作為模組實現的庫不能接受任何引數,因此嘗試為類庫指定引數會導致錯誤。

類庫所需的引數數量與類庫的建構函式所接受的引數數量相同。預設值和可變數量的引數與關鍵字引數的工作方式類似。傳遞給庫的引數以及庫名稱本身都可以使用變數來指定,因此可以透過命令列進行更改。

*** Settings ***
Library    MyLibrary     10.0.0.1    8080
Library    AnotherLib    ${VAR}

上述使用的類庫實現

from example import Connection

class MyLibrary:

    def __init__(self, host, port=80):
        self._conn = Connection(host, int(port))

    def send_message(self, message):
        self._conn.send(message)
        
class AnotherLib:

    def __init__(self, environment):
        self.environment = environment

    def do_something(self):
        if self.environment == 'test':
            # do something in test environment
        else:
            # do something in other environments

類庫作用範圍

作為類實現的庫可以具有內部狀態,該狀態可以透過關鍵字和傳遞給庫建構函式的引數進行更改。因為狀態會影響關鍵字的實際行為,所以確保一個測試用例中的更改不會意外影響其他測試用例是很重要的。這種依賴關係可能會產生難以除錯的問題,例如,當新增新的測試用例時,它們不一致地使用庫。
Robot Framework試圖保持測試用例彼此獨立:預設情況下,它為每個測試用例建立新的測試庫例項。然而,這種行為並不總是可取的,因為有時測試用例應該能夠共享一個公共狀態。此外,所有庫都沒有狀態時,根本不需要建立庫的新例項。
測試庫可以控制何時使用類屬性ROBOT_IBRARY_SCOPE建立庫。此屬性必須是字串,並且可以具有以下三個值:

  • TEST

    為每個測試用例建立一個新例項。套件setup和套件teardown共享另一個例項。在Robot Framework 3.2之前,此值為TEST CASE,但現在建議使用TEST。因為所有未識別的值都被認為與TEST相同,所以這兩個值都適用於所有版本。出於同樣的原因,如果庫的目標用於RPA,而非測試時,也可以使用值TASK。如果未設定ROBOT_LIBRARY_COPE屬性時,預設為TEST

  • SUITE

    將為每個測試套件建立一個新例項。從測試用例檔案建立幷包含測試用例的最低階別的測試套件都有自己的例項,而更高階別的套件都有各自的例項用於可能的setupteardown。在Robot Framework 3.2之前,此值為TEST SUITE。該值仍然有效,但建議將SUITE用於面向Robot Framework 3.2及更新版本的庫。

  • GLOBAL

    在整個測試執行過程中只建立一個例項,它由所有測試用例和測試套件共享。從模組建立的庫始終是全域性的

注意:

如果使用不同的引數多次匯入同一個庫,則每次都會建立一個新的例項,忽略ROBOT_LIBRARY_COPE配置。

SUITEGLOBAL作用域與具有狀態的庫一起使用時,建議庫使用一些特殊關鍵字來清除狀態。例如,可以在套件setupteardown中使用此關鍵字,以確保下一個測試套件中的測試用例可以從已知狀態開始。例如,SeleniumLibrary使用GLOBAL作用域可以在不同的測試用例中使用同一個瀏覽器,而無需重新開啟它,而且它還具有Close All Browsers關鍵字,可以輕鬆關閉所有開啟的瀏覽器。

使用SUITE作用域的示例類庫:

class ExampleLibrary:
    ROBOT_LIBRARY_SCOPE = 'SUITE'

    def __init__(self):
        self._counter = 0

    def count(self):
        self._counter += 1
        print(self._counter)

    def clear_counter(self):
        self._counter = 0

類庫版本

當使用測試庫時,Robot Framework會嘗試確定其版本。然後將這些資訊寫入syslog,以提供除錯資訊。庫文件工具Libdoc也將這些資訊寫入它生成的關鍵字文件中。

從屬性ROBOT_IBRARY_VERSION讀取版本資訊,類似於從ROBOT_LIBARY_som讀取庫作用範圍。如果ROBOT_IBRARY_VERSION不存在,則嘗試從__version__屬性讀取資訊。這些屬性必須是類或模組屬性,這取決於庫是作為類還是模組實現的。

使用__version__的示例模組:

__version__ = '0.1'

def keyword():
    pass

建立關鍵字

什麼方法被視為關鍵字

當使用靜態庫API時,Robot Framework使用反射來找出庫類或模組實現了哪些關鍵字。預設情況下,它不包括以下劃線開頭的方法和函式所有未被忽略的方法和函式都被視為關鍵字。例如,下面的庫實現了單個關鍵字My keyword

class MyLibrary:

    def my_keyword(self, arg):
        return self._helper_method(arg)

    def _helper_method(self, arg):
        return arg.upper()

限制public方法成為關鍵字

自動將所有公有方法和函式視為關鍵字通常效果良好,但有時候我們不期望這樣。例如,當將庫實現為類時,可能基類中的方法也被視為關鍵字,當將庫實現為模組時, 當將庫實現為模組時,匯入到模組名稱空間的函式也會自動成為關鍵字,這些可能不是我們想要的。

本節介紹如何防止方法和函式成為關鍵字。

基於類實現的類庫

當庫被實現為類時,可以透過將類屬性ROBOT_AUTO_KEYWORDS 設定為False來告訴Robot Framework不要自動將方法暴露為關鍵字:

class Example:
    ROBOT_AUTO_KEYWORDS = False

ROBOT_AUTO_KEYWORDS屬性被設定為False時,僅被使用@keyword 裝飾器 顯式修飾或以擁有robot_name屬性的方法才會變成關鍵字。 @keyword 裝飾器可以用於給關鍵字設定 自定義名稱, 標籤引數型別 )。

from robot.api.deco import keyword

class Example:
    ROBOT_AUTO_KEYWORDS = False
    
    @keyword
    def this_is_keyword(self):
        pass

    @keyword('This is keyword with custom name')
    def xxx(self):
        print('關鍵字: This is keyword with custom name')

    def this_is_not_keyword(self):
        pass

除了使用ROBOT_AUTO_KEYWORDS 屬性,還可以使用更方便的 @library 裝飾器來設定不要自動將方法暴露為關鍵字

from robot.api.deco import keyword, library


@library
class Example:

    @keyword
    def this_is_keyword(self):
        pass

    @keyword('This is keyword with custom name')
    def xxx(self):
        pass

    def this_is_not_keyword(self):
        pass

注意:

在ROBOT Framework 3.2中,使用ROBOT_AUTO_KEYWORDS 屬性和 @library 裝飾器來限制哪些方法成為關鍵字都是ROBOT Framework 3.2中新增的。
顯式指定庫實現的關鍵字的另一種方法是使用dynamic或者 hybrid 庫api。

基於模組實現的類庫

預設情況下,將庫實現為模組時,模組名稱空間中的所有函式都將成為關鍵字。對於匯入的函式也是如此。例如,如果將以下模組用作類庫,則它將包含關鍵字 Example Keyword,此外還包含關鍵字Current Thread

from threading import current_thread


def example_keyword():
    print('Running in thread "%s".' % current_thread().name)

避免匯入的函式成為關鍵字的一個簡單方法是隻匯入模組(例如import threading),並透過模組使用函式(例如threading.current_thread())或者,可以在匯入時為函式提供一個以下劃線開頭的別名(例如from threading import current_thread as _current_thread

限制函式成為關鍵字的一種更明確的方法是使用模組級__all__屬性,Python本身也將其用於類似的目的。如果使用它,則只有該屬性列出的函式才可以成為關鍵字。例如,下面的庫只實現了一個關鍵字Example Keyword

from threading import current_thread


__all__ = ['example_keyword']


def example_keyword():
    print('Running in thread "%s".' % current_thread().name)

def this_is_not_keyword():
    pass

如果類庫很大,那麼在新增、刪除或重新命名關鍵字時維護__all__屬性可能是一項艱鉅的任務。顯式標記哪些函式是關鍵字的另一種方法是使用ROBOT_AUTO_KEYWORDS 屬性。當該屬性設定為False值時,只有用 [@keyword裝飾器顯式修飾的函式才會成為關鍵字。例如,此庫也僅實現一個關鍵字Example Keyword

from threading import current_thread

from robot.api.deco import keyword


ROBOT_AUTO_KEYWORDS = False


@keyword
def example_keyword():
    print('Running in thread "%s".' % current_thread().name)

def this_is_not_keyword():
    pass

注意:

使用 ROBOT_AUTO_KEYWORDS 限制哪些函式成為關鍵字是ROBOT Framework 3.2中的一項新功能。

使用@not_keyword 裝飾器

模組中的函式和類中的方法可以透過使用@not_keyword裝飾器顯式標記為非關鍵字。當一個庫被實現為模組時,這個裝飾器也可以用來避免匯入的函式變成關鍵字。

from threading import current_thread

from robot.api.deco import not_keyword


not_keyword(current_thread)    # Don't expose `current_thread` as a keyword.


def example_keyword():
    print('Running in thread "%s".' % current_thread().name)

@not_keyword
def this_is_not_keyword():
    pass

與使用@library裝飾器禁用自動關鍵字發現或將ROBOT_AUTO_KEYWORDS設定為False值相比,使用@not_keyword裝飾器是避免函式或方法成為關鍵字的完全相反的方法。使用哪一個取決於上下文。

注意:

@not_keyword 是ROBOT Framework 3.2中新增功能。

關鍵字名稱

Similarly both the do_nothing and doNothing methods can be used as the Do Nothing keyword in the test data.

將測試資料中使用的關鍵字名稱與方法名稱進行比較,以找到實現這些關鍵字的方法。名稱比較不區分大小寫,並且會忽略空格和下劃線。例如,方法hello對映到關鍵字名稱Hellohello甚至h e l l o。類似地,do_nothingdoNothing方法都可以用作測試資料中的Do Nothing關鍵字。

示例庫在MyLibrary.py檔案中實現為模組:

def hello(name):
    print("Hello, %s!" % name)

def do_nothing():
    pass

以下示例說明了如何使用上面的示例庫。如果您想自己嘗試,請確保庫位於模組搜尋路徑.

*** Settings ***
Library    MyLibrary

*** Test Cases ***
My Test
    Do Nothing
    Hello    world
設定自定義名稱

可以為關鍵字暴露一個不同的名稱,而不是對映到方法名稱的預設關鍵字名稱。這可以透過將方法上的robot_name屬性設定為所需的自定義名稱來實現

def login(username, password):
    print('login')
    
login.robot_name = 'Login via user panel'
*** Test Cases ***
My Test
    Login Via User Panel    ${username}    ${password}

相比上述顯示設定robot_name 屬性,通常最簡單的是使用 @keyword 裝飾器

from robot.api.deco import keyword


@keyword('Login via user panel')
def login(username, password):
    # ...

使用不帶引數的該裝飾器不會對暴露的關鍵字名稱產生影響,但仍會設定robot_name屬性。這允許標記方法為關鍵字,但不會真的更改關鍵字名稱。即使方法名稱本身以下劃線開頭,具有robot_name屬性的方法也會建立關鍵字。

設定自定義關鍵字名稱還可以使庫關鍵字使用嵌入式引數 語法接收引數。

關鍵字標籤(tag)

類庫關鍵字和使用者關鍵字 可以擁有標籤。可以透過在方法上設定 robot_tags 屬性來定義類庫關鍵字的標籤:

from robot.api.deco import keyword


@keyword(tags=['tag1', 'tag2'])
def login(username, password):
    # ...

@keyword('Custom name', ['tags', 'here'])
def another_example():
    # ...

Another option for setting tags is giving them on the last line of keyword documentation with Tags: prefix and separated by a comma. For example:

設定tag的另一個方法是在關鍵字文件的最後一行新增標籤:Tags:打頭,以逗號分隔的標籤名稱。

例如

def login(username, password):
    """Log user in to SUT.

    Tags: tag1, tag2
    """
    # ...

關鍵字引數

使用靜態和混合(hybrid)API,可以直接從實現關鍵字的方法獲得關鍵字需要多少個引數的資訊。使用 動態庫API (https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dynamic-libraryapi)有其他方式來共享這些資訊,因此本節與它們無關。

最常見也是最簡單的情況是關鍵字需要確切數量的引數,在這種情況下,該方法只需接受這些引數。例如,一個實現不帶引數的關鍵字方法也不接收引數,一個實現只帶一個引數的關鍵字實現方法也接收一個引數,依此類推。

接收不同數量的引數的關鍵字示例

def no_arguments():
    print("Keyword got no arguments.")

def one_argument(arg):
    print("Keyword got one argument '%s'." % arg)

def three_arguments(a1, a2, a3):
    print("Keyword got three arguments '%s', '%s' and '%s'." % (a1, a2, a3))

關鍵字預設值

關鍵字使用的某些引數具有預設值,這通常很有用。

在Python中,一個方法總是隻有一個實現,並且可能的預設值在方法定義中指定。下面展示了所有Python程式設計師都熟悉的語法

def one_default(arg='default'):
    print("Argument has value %s" % arg)

def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
    print("Got arguments %s, %s and %s" % (arg1, arg2, arg3))

上面的第一個示例關鍵字可以與零個或一個引數一起使用。如果沒有給定任何引數,arg將取預設值default。如果有一個引數,arg將獲得該值,並且使用多個引數呼叫關鍵字將失敗。在第二個示例中,始終需要一個引數,但第二個和第三個引數具有預設值,因此可以將關鍵字與一到三個引數一起使用。

*** Test Cases ***
Defaults
    One Default
    One Default    argument
    Multiple Defaults    required arg
    Multiple Defaults    required arg    optional
    Multiple Defaults    required arg    optional 1    optional 2

可變數量的引數 (*varargs)

Robot Framework還支援接受任意數量引數的關鍵字。
Python支援接受任意數量引數的方法。相同的語法在庫中也有效,如下面的示例所示,它也可以與其他指定引數的方式組合使用:

def any_arguments(*args):
    print("Got arguments:")
    for arg in args:
        print(arg)

def one_required(required, *others):
    print("Required: %s\nOthers:" % required)
    for arg in others:
        print(arg)

def also_defaults(req, def1="default 1", def2="default 2", *rest):
    print(req, def1, def2, rest)
*** Test Cases ***
Varargs
    Any Arguments
    Any Arguments    argument
    Any Arguments    arg 1    arg 2    arg 3    arg 4    arg 5
    One Required     required arg
    One Required     required arg    another arg    yet another
    Also Defaults    required
    Also Defaults    required    these two    have defaults
    Also Defaults    1    2    3    4    5    6

自由關鍵詞引數(**kwargs)

Robot Framework支援Python的**kwargs語法。在本節中,我們將瞭解如何建立這樣的關鍵字。
如以下示例顯示了基本功能:

def example_keyword(**stuff):
    for name, value in stuff.items():
        print(name, value)
*** Test Cases ***
Keyword Arguments
    Example Keyword    hello=world        # Logs 'hello world'.
    Example Keyword    foo=1    bar=42    # Logs 'foo 1' and 'bar 42'.

通常,位於關鍵字最後的所有引數都使用命名語法規則name=value,並且與任何其他引數都不匹配的,將作為kwargs傳遞給關鍵字。為了避免使用像foo=quux這樣的字面值作為自由關鍵字引數,必須對其進行轉義比如foo\=quux

以下示例說明了普通位置引數、可變引數和關鍵詞引數

def various_args(arg=None, *varargs, **kwargs):
    if arg is not None:
        print('arg:', arg)
    for value in varargs:
        print('vararg:', value)
    for name, value in sorted(kwargs.items()):
        print('kwarg:', name, value)
        
*** Test Cases ***
Positional
    Various Args    hello    world                # Logs 'arg: hello' and 'vararg: world'.

Named
    Various Args    arg=value                     # Logs 'arg: value'.

Kwargs
    Various Args    a=1    b=2    c=3             # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
    Various Args    c=3    a=1    b=2             # Same as above. Order does not matter.

Positional and kwargs
    Various Args    1    2    kw=3                # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.

Named and kwargs
    Various Args    arg=value      hello=world    # Logs 'arg: value' and 'kwarg: hello world'.
    Various Args    hello=world    arg=value      # Same as above. Order does not matter.

僅限關鍵詞引數

從Robot Framework 3.1開始,可以對關鍵字使用僅命名引數。Python的僅關鍵字引數提供了這種支援。 僅關鍵字引數在的*varargs之後指定,或者在不需要*varargs時在專用的*標記之後指定。可能的**kwargs是在僅關鍵字引數之後指定的。

例子:

def sort_words(*words, case_sensitive=False):
    key = str.lower if case_sensitive else None
    return sorted(words, key=key)

def strip_spaces(word, *, left=True, right=True):
    if left:
        word = word.lstrip()
    if right:
        word = word.rstrip()
    return word
    
*** Test Cases ***
Example
    Sort Words    Foo    bar    baZ
    Sort Words    Foo    bar    baZ    case_sensitive=True
    Strip Spaces    ${word}    left=False

僅限位置引數

Python支援所謂的僅限位置引數 ,這讓指定指定某個引數智慧作為位置引數提供,而非命名引數入 name=value。僅限位置引數在普通引數之前指定,並且在它們之後必須指定特殊的/標記

def keyword(posonly, /, normal):
    print(f"Got positional-only argument {posonly} and normal argument {normal}.")

可以這樣使用上面的關鍵字:

*** Test Cases ***
Example
    # Positional-only and normal argument used as positional arguments.
    Keyword    foo    bar
    # Normal argument can also be named.
    Keyword    foo    normal=bar

如果僅限位置引數與包含等號的值(如example=usage)一起使用,則不被認為是命名引數語法,即使=之前的部分與引數名稱匹配。此規則僅適用於僅限位置引數在其正確位置使用而沒有其他引數在其前面使用名稱引數語法的情況。

*** Test Cases ***
Example
    # Positional-only argument gets literal value `posonly=foo` in this case.
    Keyword    posonly=foo    normal=bar
    # This fails.
    Keyword    normal=bar    posonly=foo

從Robot Framework 4.0開始,完全支援僅限位置引數。將它們用作位置引數也適用於早期版本,但將它們用作命名引數會導致Python方面的錯誤。

......

更多詳細資訊請參考官方文件

參考連線

https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#creating-test-libraries

https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#setting-custom-name-to-library

相關文章