【AltWalker】模型驅動:輕鬆實現自動化測試用例的生成和組織執行

2023-07-27 21:00:39

模型驅動的自動化測試

模型驅動的自動化測試(Model-Based Testing, 後文中我們將簡稱為MBT)是一種軟體測試方法,它將系統的行為表示為一個或多個模型,然後從模型中自動生成和執行測試用例。這種方法的核心思想是將測試過程中的重點從手動編寫測試用例轉移到建立和維護描述系統行為的模型。

優勢

基於MBT的測試相比較於普通的測試有以下幾大優點:

優點 描述
覆蓋率 模型是對系統行為的抽象表示,可以幫助測試人員更好地理解和分析系統的功能和效能。從而提高測試覆蓋率。
測試效率 通過從模型中自動生成測試用例,減少手動編寫測試用例的工作量,提高測試效率。
可維護性 模型驅動的測試用例易於維護,因為當系統發生變化時,只需更新模型,而無需手動修改大量測試用例。
可重用性 模型可以在多個測試專案中重用,減少重複工作並提高測試質量。

操作步驟

基於MBT的測試主要有以下幾個步驟:

  • 建立模型:
    首先,需要構建一個描述系統行為的模型。這個模型通常採用圖形表示,如狀態機、Petri網或者流程圖等。模型中的頂點表示系統的狀態,邊表示狀態之間的轉換。

  • 生成測試用例:
    通過分析模型,可以自動生成測試用例。測試用例生成演演算法可以根據不同的目標(如覆蓋率、路徑長度等)來選擇。常用的演演算法有隨機測試、覆蓋所有路徑、覆蓋所有邊等。

  • 執行測試:
    使用自動生成的測試用例對實際系統進行測試。這一步通常需要一個測試執行引擎,它可以將模型中的操作對映到實際系統中的操作。例如,使用Selenium WebDriver進行Web應用測試。

  • 驗證結果:
    比較實際系統的行為與模型的預期行為,以確定系統是否滿足需求。如果發現問題,可以更新模型並重新生成測試用例。

下面我們一起來看看在實際業務中使用AltWalker庫進行進行基於模型的自動化測試的應用。

什麼是AltWalker?

AltWalker是一個基於圖模型的自動化測試框架,用於編寫、執行和管理基於模型的測試。它主要用於測試複雜系統,如Web應用程式、移動應用程式等。它支援執行用 .NET/C#Python3 編寫測試模型用例。

安裝AltWalker

在命令列中輸入以下命令來安裝AltWalker:

pip install altwalker
檢查是否安裝了正確的版本
$ altwalker --version
AltWalker, version 0.3.1

牛刀小試

建立一個測試專案

建立一個新的資料夾,用於存放我們的測試專案。在該資料夾中,執行以下命令建立MBT

altwalker init -l python test-project

執行這個命令後,將在當前目錄生成一個包含模型模板程式碼的目錄test-project,目錄結構如下:

# tree
test-project/
├── models/
│   ├── default.json
└── tests/
    ├── __init__.py
    └── test.py
執行測試

只需要執行以下命令,就能夠執行default.json中定義的模型,覆蓋模型的鏈路,鏈路執行時會執行對應在test.py中的程式碼

$ cd test-project
$ altwalker online tests -m models/default.json "random(edge_coverage(100))"
執行效果

自己編寫圖模型

上面只是執行了一個單一的鏈路Demo,下面我們一起寫一個我們自己的模型。

模型編輯器我們可以選擇線上的,也可以選擇使用 vscode的外掛,當然也可以選擇自己搭建一個。

線上模型編輯器

我們可以使用線上的模型編輯器來編寫模型。

VScode擴充套件

可以在擴充套件上搜尋AltWalker Model Visualizer
,也可以自己存取連結下載擴充套件來編輯檢視模型。

本地部署

可以參考:https://github.com/altwalker/model-editor

包含登入、選擇產品、支付、退出登入的模型編寫

點選檢視對應模型
{
    "name": "Pay Models",
    "models": [
      {
        "name": "PayModel",
        "generator": "random(edge_coverage(100) && vertex_coverage(100))",
        "startElementId": "v_init",
        "vertices": [
          {
            "id": "v_init",
            "name": "start_vertex"
          },
          {
            "id": "v_login",
            "name": "state_login"
          },
          {
            "id": "v_select_product",
            "name": "state_select_product"
          },
          {
            "id": "v_pay",
            "name": "state_pay"
          },
          {
            "id": "v_logout",
            "name": "state_logout"
          }
        ],
        "edges": [
          {
            "id": "e_init",
            "name": "action_init",
            "sourceVertexId": "v_init",
            "targetVertexId": "v_login"
          },
          {
            "id": "e_login",
            "name": "action_login",
            "sourceVertexId": "v_login",
            "targetVertexId": "v_select_product"
          },
          {
            "id": "e_select_product",
            "name": "action_select_product",
            "sourceVertexId": "v_select_product",
            "targetVertexId": "v_pay"
          },
          {
            "id": "e_pay",
            "name": "action_pay",
            "sourceVertexId": "v_pay",
            "targetVertexId": "v_logout"
          },
          {
            "id": "e_logout",
            "name": "action_logout",
            "sourceVertexId": "v_logout",
            "targetVertexId": "v_init"
          }
        ]
      }
    ]
  }
模型效果

1、校驗模型

儲存這個模型到新建立的專案default.json,然後執行下面的命令檢查模型是否存在問題:

altwalker check -m models/default.json "random(never)" 
2、驗證測試程式碼是否和模型是否匹配
altwalker check -m models/default.json "random(never)" 

因為我們還沒有編寫測試程式碼,所以會出現下面的報錯:

可以看到在上面的報錯中給出了建議的程式碼,我們把它複製到 test.py中。

class PayModel:

    def state_pay(self):
        pass

    def action_init(self):
        pass

    def state_select_product(self):
        pass

    def state_logout(self):
        pass
        ...

3、執行測試

在命令列中執行下面的命令來執行測試:

altwalker online tests -m models/default.json "random(edge_coverage(100))" --report-xml-file report.xml

這將會執行在default.jsontest.py檔案中定義的所有測試用例。

4、檢視測試報告

在測試執行完成後,AltWalker會生成一個名為report.xml的JUnit格式的測試報告。我們可以使用任何支援JUnit格式的測試報告工具來檢視這個報告。

還有其他方式的測試報告提供,可以檢視官方檔案。

一個比較複雜的例子

如果我們有一個商城需要驗證,有登入、使用者個人中心,商品首頁,商品詳情頁、支付介面、訂單介面等,我們要針對這樣的網站做一個自動化,可能會有以下這些場景:

場景
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選第二個商品按鈕 -> 商品C詳情頁面 -> Do:點選付款 -> 付款介面 -> Do:取消付款 -> 訂單倒計時介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選第二個商品按鈕 -> 商品C詳情頁面 -> Do:點選付款 -> 付款介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選第二個商品按鈕 -> 商品C詳情頁面 -> Do:點選付款 -> 付款介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:檢視訂單詳情 -> 訂單詳情介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選第二個商品按鈕 -> 商品C詳情頁面 -> Do:點選使用者中心 -> 使用者中心介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選第二個商品按鈕 -> 商品C詳情頁面 -> Do:點選使用者中心 -> 使用者中心介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:檢視訂單詳情 -> 訂單詳情介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選相似商品B -> 商品B詳情頁 -> Do:點選購買 -> 付款介面 -> Do:取消付款 -> 訂單倒計時介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選相似商品B -> 商品B詳情頁 -> Do:點選購買 -> 付款介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
登入介面 -> Do:輸入密碼登入 -> 使用者首頁 -> Do:檢視商品A -> A商品詳情頁 -> Do:點選相似商品B -> 商品B詳情頁 -> Do:點選購買 -> 付款介面 -> Do:檢視訂單 -> 歷史訂單介面 -> Do:檢視訂單詳情 -> 訂單詳情介面 -> Do:關閉瀏覽器 -> 瀏覽器關閉
...
使用 MBT 圖示展示(箭頭表示操作)

大家應該都能發現,使用altwalker畫出來的圖,能非常直觀的展示各個頁面之間可以進行的操作,並且很好擴充套件,這非常符合我們做頁面自動化的時候所說的POM(Page Object Model)。

這也是altwalker最重要的價值所在:適合的用於測試複雜系統,如Web應用程式、移動應用程式等。

點選檢視對應模型
{
    "name": "Default Models",
    "models": [
        {
            "name": "DefaultModel",
            "generator": "random(never)",
            "startElementId": "v0",
            "vertices": [
                {
                    "id": "v0",
                    "name": "登入介面"
                },
                {
                    "id": "v1",
                    "name": "使用者首頁"
                },
                {
                    "id": "v2",
                    "name": "瀏覽器關閉"
                },
                {
                    "id": "v3",
                    "name": "A商品詳情頁"
                },
                {
                    "id": "v6",
                    "name": "商品C詳情頁面"
                },
                {
                    "id": "v7",
                    "name": "付款介面"
                },
                {
                    "id": "v8",
                    "name": "付款成功介面"
                },
                {
                    "id": "v9",
                    "name": "訂單倒計時介面"
                },
                {
                    "id": "v10",
                    "name": "使用者中心介面"
                },
                {
                    "id": "v11",
                    "name": "商品B詳情頁"
                },
                {
                    "id": "v4",
                    "name": "歷史訂單介面"
                },
                {
                    "id": "v5",
                    "name": "訂單詳情介面"
                }
            ],
            "edges": [
                {
                    "id": "e0",
                    "name": "輸入密碼登入",
                    "sourceVertexId": "v0",
                    "targetVertexId": "v1"
                },
                {
                    "id": "e1",
                    "name": "關閉瀏覽器",
                    "sourceVertexId": "v0",
                    "targetVertexId": "v2"
                },
                {
                    "id": "e2",
                    "name": "關閉瀏覽器",
                    "sourceVertexId": "v1",
                    "targetVertexId": "v2"
                },
                {
                    "id": "e3",
                    "name": "檢視商品A",
                    "sourceVertexId": "v1",
                    "targetVertexId": "v3"
                },
                {
                    "id": "e5",
                    "name": "點選第二個商品按鈕",
                    "sourceVertexId": "v3",
                    "targetVertexId": "v6"
                },
                {
                    "id": "e7",
                    "name": "點選付款",
                    "sourceVertexId": "v6",
                    "targetVertexId": "v7"
                },
                {
                    "id": "e8",
                    "name": "退出登入",
                    "sourceVertexId": "v8",
                    "targetVertexId": "v0"
                },
                {
                    "id": "e9",
                    "name": "e9",
                    "sourceVertexId": "v3",
                    "targetVertexId": "v8"
                },
                {
                    "id": "e10",
                    "name": "付款成功",
                    "sourceVertexId": "v7",
                    "targetVertexId": "v8"
                },
                {
                    "id": "e11",
                    "name": "取消付款",
                    "sourceVertexId": "v7",
                    "targetVertexId": "v9"
                },
                {
                    "id": "e12",
                    "name": "點選使用者中心",
                    "sourceVertexId": "v6",
                    "targetVertexId": "v10"
                },
                {
                    "id": "e13",
                    "name": "點選購買",
                    "sourceVertexId": "v11",
                    "targetVertexId": "v7"
                },
                {
                    "id": "e14",
                    "name": "點選相似商品B",
                    "sourceVertexId": "v3",
                    "targetVertexId": "v11"
                },
                {
                    "id": "e17",
                    "name": "關閉瀏覽器",
                    "sourceVertexId": "v9",
                    "targetVertexId": "v2"
                },
                {
                    "id": "e18",
                    "sourceVertexId": "v10",
                    "targetVertexId": "v4",
                    "name": "檢視訂單"
                },
                {
                    "id": "e19",
                    "name": "點選首頁按鈕",
                    "sourceVertexId": "v3",
                    "targetVertexId": "v1"
                },
                {
                    "id": "e20",
                    "name": "點選首頁",
                    "sourceVertexId": "v6",
                    "targetVertexId": "v1"
                },
                {
                    "id": "e21",
                    "name": "點選使用者中心",
                    "sourceVertexId": "v10",
                    "targetVertexId": "v1"
                },
                {
                    "id": "e4",
                    "name": "重新整理瀏覽器",
                    "sourceVertexId": "v6",
                    "targetVertexId": "v6"
                },
                {
                    "id": "e6",
                    "name": "檢視訂單",
                    "sourceVertexId": "v7",
                    "targetVertexId": "v4"
                },
                {
                    "id": "e15",
                    "name": "關閉瀏覽器",
                    "sourceVertexId": "v4",
                    "targetVertexId": "v2"
                },
                {
                    "id": "e16",
                    "name": "檢視訂單詳情",
                    "sourceVertexId": "v4",
                    "targetVertexId": "v5"
                },
                {
                    "id": "e22",
                    "name": "關閉瀏覽器",
                    "sourceVertexId": "v5",
                    "targetVertexId": "v2"
                }
            ],
            "actions": [
                "fasf;fdsaf;"
            ]
        }
    ]
}

完整的結合 POM使用的方法可以檢視官方給出的範例:https://github.com/altwalker/altwalker-examples

結合已有框架使用

如果我們僅僅只想使用模型中的鏈路組織能力,也可以自己根據編寫的模型使用下面這段程式碼來生成對應的鏈路,然後組織用例場景,可以用於自動化用例生成。

graph_data = {
    "name": "Default Models",
    "models": [
        {
            "name": "DefaultModel",
            "generator": "random(never)",
            "startElementId": "v0",
            "vertices": [
                {
                    "id": "v0",
                    "name": "登入介面"
        ...
    ]
}
model = graph_data['models'][0]
edges = model['edges']
vertices = model['vertices']
start_vertex_id = model['startElementId']

# 構建圖
graph = {}
for edge in edges:
    source = edge['sourceVertexId']
    target = edge['targetVertexId']
    if source not in graph:
        graph[source] = []
    graph[source].append((target, edge['name']))

# 頂點ID到頂點名稱的對映
vertex_name_map = {vertex['id']: vertex['name'] for vertex in vertices}


# 深度優先搜尋
def dfs(_graph, start_vertex, _path, _visited):
    _visited.add(start_vertex)
    _path.append(vertex_name_map[start_vertex])
    if start_vertex not in _graph:
        print(" -> ".join(_path))
    else:
        for neighbor, action in _graph[start_vertex]:
            if neighbor not in _visited:
                _path.append(f"Do:{action}")
                dfs(_graph, neighbor, _path, _visited)
                _path.pop()
    _path.pop()
    _visited.remove(start_vertex)


# 列印所有可能的鏈路及其經過的操作,操作前面加上"Do"標記
visited = set()
path = []
dfs(graph, start_vertex_id, path, visited)

總結

通過以上步驟,我們瞭解瞭如何使用AltWalker進行模型驅動的自動化測試。AltWalker是一個強大的測試框架,可以幫助我們更高效地編寫、執行和管理測試用例。

當然,基於模型的測試也有一些侷限性,如模型的準確性和完整性對測試結果影響較大,模型構建和維護可能需要額外的成本等。因此,在實際應用中,需要根據專案的具體需求來決定是否採用基於模型的測試方法。希望本文對你有所幫助!