1. 程式人生 > >自動化測試框架思路簡單分享

自動化測試框架思路簡單分享

轉載地址:https://testerhome.com/topics/2775

現在有許多的自動化測試框架可以使用,如 appium,xUnit,Cucumber 等,但很多時候單純使用其中一個框架並不是十分好用,而且很多的框架名詞,如 BDD,關鍵字驅動等也會讓一些想接觸這方面的人感到有點 Hold 不住。其實一個完整、實用的框架是有規律可循的,而且也並不是特別困難。

下面的大部分思路來自於 羋峮 大神的 《iOS 測試指南》 中 8.6 自動化測試框架剖析 。看完這裡後我覺得我終於把心中的混亂理清了。因此我也在此介紹給大家,理清一些混亂的地方。

測試框架分層

一個完整的測試框架是怎樣的?

例如我們寫 appium 的測試用例,如果只是做 Demo 的話我們會直接使用 WebDriver API 來寫。這時候一個用例長這樣:

import os
from time import sleep
from appium import webdriver

if __name__ == '__main__':
    # Returns abs path relative to this file and not cwd
    PATH = lambda p: os.path.abspath(
        os.path.join(os.path.dirname(__file__
), p) ) # init driver, open session desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['platformVersion'] = '4.2' desired_caps['deviceName'] = 'Android Emulator' desired_caps['app'] = PATH( '../../../sample-code/apps/ApiDemos/bin/ApiDemos-debug.apk'
) driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) # execute some action els = driver.find_elements_by_android_uiautomator("new UiSelector().clickable(true)") # check if result is as expected if len(els) != 12: print "Case Failed. There should be 12 elements but only {} elements exist".format(len(els)) else: print "Case Passed." # end the session driver.quit()

然後用例數目比較多了,每個 case 裡面都要啟動 appium 太麻煩了,而且測試結果也不夠好看,所以我們開始使用一些單元測試框架,使用它們的 setUp,tearDown 或者測試報告。此時用例會長這樣:

import os
from time import sleep

import unittest

from appium import webdriver

# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
    os.path.join(os.path.dirname(__file__), p)
)

class SimpleAndroidTests(unittest.TestCase):
    def setUp(self):
        desired_caps = {}
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '4.2'
        desired_caps['deviceName'] = 'Android Emulator'
        desired_caps['app'] = PATH(
            '../../../sample-code/apps/ApiDemos/bin/ApiDemos-debug.apk'
        )

        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        # end the session
        self.driver.quit()

    def test_check_clickable_element_count(self):

        els = self.driver.find_elements_by_android_uiautomator("new UiSelector().clickable(true)")
        self.assertEqual(12, len(els))

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(SimpleAndroidTests)
    unittest.TextTestRunner(verbosity=2).run(suite)

再後面我們要寫更多的用例,單靠做自動化測試的幾個人 hold 不住了,需要讓一些技術方面不那麼強的人來做。為了讓他們容易上手而又不至於搞亂我們的框架,我們會把用例再封裝,做成表格或者 BDD 這種不會程式碼的人也能比較容易寫用例和讀用例的形式。此時用例會長這樣 (Cucumber):

Feature: Simple android test
  Do some sample operations with android application

  Scenario: Check clickable element count
    When I opened the application
    Then 12 clickable buttons should occur

這三步其實對應著測試框架的三個層級:工具層(如 appium),核心層(如單元測試框架),適配層(如 BDD 框架)

工具層

工具層主要負責相應側面的測試執行的動作觸發。(書上原文)

首先,我們要想通過程式控制一些被測程式,那麼就必須有相應的工具。這些工具讓原本難以做到、甚至無法做到的控制方式變成了可能,同時也讓它們更容易被做到。例如 iOS 的 UIAutomation 。如果沒有它的存在,我們就必須在應用里加入一些 agent (如 MonkeyTalk ),或者直接在應用裡新增測試程式碼(如 KIF ),甚至自己做一套能通過介面控制程式的測試工具。這些工具能讓我們節省很多的時間,並且更簡單地做到測試部分和功能部分的分離。

工具層的框架很多,移動測試的有 Appium,robotium,espresso 等,web 測試基本是 selenium 的天下。

核心層

核心層負責測試執行的驅動和結果監控並且反饋。(書上原文)

自動化測試程式其實都有一些共通之處,例如它們都需要豐富的斷言功能支援,否則就只能去寫各種 If..else..,然後校驗和執行步驟就耦合到一起了。核心層的框架做的就是這個事情,把自動化測試程式這種程式的最共通之處抽取出來(例如需要有專門的異常對應 fail,需要有各種靈活簡便的斷言,需要有創造前置條件的專用函式),然後以一種較為固定的寫法把它們封裝起來。

例如大部分用例都會有前置條件(如 web 的用例會要求瀏覽器必須處於開啟狀態),而且會高度重複,並具有 block 的特性(瀏覽器沒開啟,後面的步驟就不用執行了)。那麼核心層的框架就會提供一個特殊的方法(一般方法名為 setUp),讓這個方法在執行用例的內容前執行以創造合適的前置條件,並在這個方法出錯或 fail 時自動把整個用例標記為 fail 或 block 。

核心層的框架也不少,例如各種單元測試框架(JUnit,OCUnit 等),Testng 等。一般來說由於工具層使用的框架選定後所使用的語言也就確定了,所以一般會先確定工具層,然後再選擇核心層使用的框架。如果語言不一致則需要適當進行二次開發來方便呼叫。當然,像 appium/selenium 這種支援多個語言的工具層框架能更靈活地選擇核心層框架,這也是它們受歡迎的重要原因。

適配層

適配層負責重複使用的測試方法封裝適配。(書上原文)

當用例達到一定的數量級(數十或者過百)時,如果沒有很好地把重複部分提取出來或者以更好的形式編寫,將會出現一定數量的冗餘程式碼。此時適配層的作用就是把這些重複的方法進行更好的封裝,讓寫用例的人能更快速地編寫/閱讀用例。

我們平時聽到的 資料驅動、關鍵字驅動、行為驅動(BDD) 大多屬於這一層。因為無論使用什麼驅動,它都和具體的測試程式/測試領域無關。你不會說 BDD 只能拿來測 Android 應用,也不會說 資料驅動 只能拿 Java 來寫。這些 xx驅動 主要是一種思想,一種實踐中好用、實用、且有不少工具可供選擇的用例編寫思想。當然由於具體使用的工具不同,所使用的語言也會有一定的限制,但思想是相通的。

適配層也有各種框架,如 BDD 的 Cucumber ,關鍵字驅動的 robot framework (同時它也帶有核心層和工具層,是一個完整的且實用的測試框架,它的工具層以擴充套件庫的形式提供,十分易於擴充套件以適應不同軟體領域,很值得借鑑)。由於適配層的編寫方式很可能已經不屬於某種具體的程式語言了(如 關鍵字驅動 使用表格的形式,脫離了語言),因此適配層的框架大多都會有對應的核心層/工具層框架提供一體化的支援。

一個簡單的例子

robot framework 相信不少人有用過。它是一個完整的帶有這三層結構的測試框架:

適配層:用例使用 tsv 格式編寫,採用的主要是關鍵字驅動的形式。
核心層:提供了 setUp,tearDown 方法,並有合適並足夠的斷言功能和異常捕獲功能。
工具層:具有眾多的 Library 可供選擇,可以結合不同的工具測試各種領域的軟體。

另一個簡單的例子

另一個例子是一種關鍵字驅動框架的實現形式:

適配層:以表格形式展現的用例,以及把用例轉換成可執行程式碼的轉換器:

表格:

action params
openBrowser browserName="Chrome"

轉換後代碼:

class TestCase(ActionBase, unittest.TestCase):

    # a test case
    def open_browser(self):
        # step of test case
        self.action_call("openBrowser", {"browserName":"Chrome"})

然後這個 action_call 的實現裡呼叫對應的 action 方法來執行實際動作:

class ActionBase:
    ...
    def action_call(action_name, params):

        # get action function in this class
        action_fun = getattr(self, action_name)

        # execute action
        action_fun(**kwargs)

    ...
    def openBrowser(browserName=None):
        if browserName == 'Chrome':
            self.driver = webdriver.Chrome()
        ...


核心層:轉換後代碼使用了 unittest 這個框架來管理用例執行

工具層:實際執行 action 時使用了 selenium 框架

總結

"Unix哲學"的根本原則: 儘量用簡單的方法解決問題 (出自 關於Unix哲學) 其實也適用於自動化框架的設計。每個框架專心做好自己要做的事情,然後通過不同框架的組合就能做出靈活適應各種專案的完整的自動化測試框架。

再好的框架也只是工具。一個好的測試專案不僅僅需要合適的框架,更需要好的用例設計、執行策略等非技術因素。因此不要只追求好的測試框架,而忽略了其他。