共用方式為


使用 Visual Studio Code 從 Azure Logic Apps 中執行的標準工作流程建立單元測試

適用於:Azure Logic Apps (標準)

單元測試是讓您的應用程式或解決方案在整個軟體開發生命週期中保持可靠且精確的基本做法。 單元測試可協助您有效率且有系統地驗證解決方案中的關鍵元件。

針對標準邏輯應用程式工作流程,您可以使用 Visual Studio Code 和 Azure Logic Apps (Standard) 擴充功能來建立單元測試。 這項功能可讓您使用先前執行的工作流程執行來建立單元測試,並針對邏輯應用程式解決方案支援的案例量身打造。 此方法提供下列優點:

  • 重用工作流程執行,以產生工作流程中特定操作的模擬資料。

    此數據可讓您測試工作流程,而不需要呼叫外部服務、系統或 API。 您可以節省時間,而您的工作流程會與實際的工作流程執行案例保持一致。

  • 在部署至其他環境之前,先識別並解決潛在問題,以改善工作流程品質。

  • 簡化與開發程式的單元測試整合,同時確保一致且精確的工作流程行為。

本指南說明如何從工作流程執行建立單元測試定義。 此定義會模擬來自每個工作流程作業的外部呼叫,而不需要變更工作流程邏輯。 當您從工作流程執行建立單元測試時,您會取得包含兩個資料夾的單元測試專案:

  • 包含工作流程中每個可模擬作業的強類型類別的資料夾。

  • 每個單元測試定義的資料夾,其中包含下列檔案:

    • JSON 檔案,表示工作流程中產生的模擬作業。

    • C# 檔案,其中包含您用來設定自己的判斷提示的範例類別和方法、確認工作流程如預期般運作,並確定工作流程在較大的 Azure 生態系統中可可靠且可預測地運作。

先決條件

限制和已知問題

  • 此版本目前僅支援 C# 來建立單元測試。

  • 此版本不支援非模擬的動作。 請確保在工作流程執行路徑中的所有動作都被模擬化。

  • 此版本不支援下列動作類型:

    • 整合帳戶動作
    • 資料映射動作
    • 自訂程式代碼動作
    • XML 動作
    • 液體動作
    • EDI 編碼和譯碼動作

複習基本概念

下列清單包含標準工作流程單元測試的基本但重要概念:

  • 邏輯應用程式單元測試

    插入模擬物件的受控制工作流程執行。 這些物件代表工作流程觸發程式或依賴於外部服務或系統的動作。

  • 可仿真動作

    相依於外部服務或系統的工作流程動作。 您可以將這些動作轉換成仿真的動作,以進行單元測試建立和執行。

從工作流程運行中創建單元測試

  1. 在 Visual Studio Code 中,開啟您的標準邏輯應用程式專案。

  2. 在 Visual Studio Code 工具列的 [ 執行 ] 功能表中,選取 [ 開始偵錯]。 (鍵盤:按 F5)

  3. 返回 檔案總管 視窗。 在您的專案中,展開工作流程定義資料夾。

  4. 開啟 workflow.json 快捷方式功能表,然後選取 [ 概觀]。

  5. 在 [概覽] 頁面上的 [執行歷程記錄] 下,選取要用來建立單元測試的工作流程執行。

    顯示 Visual Studio Code 的螢幕快照,其中包含標準邏輯應用程式專案、偵錯模式執行、開啟的工作流程概觀頁面,以及選取的工作流程執行。

  6. 在執行歷程記錄工具列上,選取 [從執行建立單元測試]

    此螢幕快照顯示 Visual Studio Code、標準工作流程執行歷程記錄頁面,以及用來建立單元測試的已選取命令。

  7. 提供用於單元測試、單元測試類別和 C# 檔案的名稱。

    在 [ 總管 ] 視窗中,名為 [測試 ] 的新專案資料夾會出現在邏輯應用程式專案資料夾底下。 [測試] 資料夾包含下列資料夾和檔案:

    此螢幕快照顯示 Visual Studio Code、Standard 邏輯應用程式專案,以及具有單元測試資料夾和檔案的 [測試] 資料夾。

    資料夾或檔案 說明
    Tests
    || <logic-app-name>
    Tests 資料夾中,當您將單元測試新增至邏輯應用程式專案時,會出現一個 <logic-app-name> 資料夾。
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    <logic-app-name> 資料夾中,當您新增工作流程的單元測試時,會出現一個 <workflow-name> 資料夾。
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    |||| MockOutputs
    ||||| <operation-name-outputs>cs
    <workflow-name> 資料夾中,MockOutputs 資料夾包含一個 C#(.cs)檔案,其中有工作流程中每個連接器作業的強類型類別。 每個 .cs 檔案名都會使用下列格式:

    < operation-name >[Trigger\|Action]Output.cs

    如果連接器作業具有 動態合約,那麼每一個 動態類型都會對應一個類別。 動態類型是指根據為該參數提供的值,具有不同輸入和輸出的作業參數。 您可以使用這些類別來擴充單元測試,並從頭開始建立新的模擬。
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    |||| <unit-test-name>
    ||||| <unit-test-name>-mock.json
    ||||| <unit-test-name>.cs
    <workflow-name> 資料夾中, <unit-test-name> 資料夾包含下列檔案:

    - <unit-test-name>-mock.json檔案包含根據執行建立單元測試的工作流程所生成模擬物件的 JSON 表示法。

    - 檔案 <unit-test-name>.cs 包含範例 C# 類別和方法,這些方法會使用 *-mock.json 檔案來執行和判斷提示結果。 您可以編輯此檔案,以符合您的特定測試案例。

檢閱 *-mock.json 檔案

此檔案有下列主要區段:

triggerMocks 區段

區塊 triggerMocks 包含來自工作流程觸發器的模擬結果。 此區段必須啟動工作流程執行,如下列範例所示:

{
    "triggerMocks": {
        "When_messages_are_available_in_a_queue_(peek-lock)": {
            "name": "When_messages_are_available_in_a_queue_(peek-lock)",
            "status": "Succeeded",
            "outputs": {
                "body": {
                    "contentData": {
                        "messageId": "1234",
                        "status": "new",
                        "contentType": "application/json",
                        "userProperties": {},
                        "scheduledEnqueueTimeUtc": "1/1/0001 12:00:00 AM",
                        "timeToLive": "14.00:00:00",
                        "deliveryCount": 1,
                        "enqueuedSequenceNumber": 0,
                        "enqueuedTimeUtc": "2025-04-07T01:10:09.738Z",
                        "lockedUntilUtc": "2025-04-07T01:11:09.769Z",
                        "lockToken": "78232fa8-03cf-4baf-b1db-3375a64e0ced",
                        "sequenceNumber": 5
                    }
                }
            }
        }
    },
    "actionMocks": {...}
}

actionMocks 區段

針對工作流程執行中的每個可模擬動作,區 actionMocks 段包含模擬動作,並保證工作流程的受控制執行。

{
    "triggerMocks": {...},
    "actionMocks": {
        "Call_External_API": {
            "name": "Call_External_API",
            "status": "Succeeded",
            "outputs": {
                "statusCode": 200,
                "body": {
                    "status": "Awesome!"
                }
            }
        },
        "CompleteMessage": {
            "name": "CompleteMessage",
            "status": "Succeeded",
            "outputs": {
                "statusCode": "OK",
                "body": {}
            }
        }
    }
}

檢閱單元測試 *.cs 檔案

此單元測試類別提供一個架構,可藉由模擬觸發程式和動作來測試標準邏輯應用程式工作流程。 此類別可讓您測試工作流程,而不需要實際呼叫外部服務或 API。

測試類別結構

典型的單元測試類別會使用下列結構:

[TestClass]
public class <unit-test-name>
{
    public TestExecutor TestExecutor;

    [TestInitialize]
    public void Setup()
    {
        this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config");
    }

    // Add test methods here.

    // Add helper methods here.
}

Setup() 方法

這個方法會使用測試設定組態檔的路徑來具現化 TestExecutor 類別。 方法會在每次測試執行之前執行,並建立 的新實例 TestExecutor

[TestInitialize]
public void Setup()
{
    this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config");
}

範例測試方法

下一節說明您可以在單元測試類別中使用的範例測試方法。

靜態模擬數據測試

下列方法示範如何使用靜態模擬數據來測試您的工作流程。 在此方法中,您可以完成下列工作:

  • 在您的模擬動作上設定屬性值。
  • 使用已設定的模擬數據執行工作流程。
  • 確認執行成功。
[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample1()
{
    // PREPARE mock: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    var sampleActionMock = mockData.ActionMocks["Call_External_API"];
    sampleActionMock.Outputs["your-property-name"] = "your-property-value";

    // ACT: Create the UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: testRun.Status);
}

動態模擬數據測試

下列方法示範如何搭配回呼方法使用動態模擬數據。 此方法提供兩個選項,可動態產生模擬數據:

這兩種方法都可讓您根據單元測試執行內容建立動態回應。

[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample2()
{
    // PREPARE: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    
    // OPTION 1: Define a callback class.
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        name: "Call_External_API", 
        onGetActionMock: CallExternalAPIActionMockOutputCallback);

    // OPTION 2: Define an inline lambda function.
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        name: "Call_External_API", 
        onGetActionMock: (testExecutionContext) =>
        {
            return new CallExternalAPIActionMock(
                status: TestWorkflowStatus.Succeeded,
                outputs: new CallExternalAPIActionOutput {

                    // If this account contains a JObject Body, 
                    // set the properties you want here:
                    // Body = "something".ToJObject()

                }
            );
        });
        
    // ACT: Create UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: testRun.Status);
}

錯誤案例測試

下列方法示範如何測試失敗狀況。 在此方法中,您可以完成下列工作:

  • 將仿真的動作設定為失敗,並顯示特定的錯誤碼和訊息。
  • 確認工作流程會正確處理這些錯誤狀況。
[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_FAILED_Sample3()
{
    // PREPARE: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    var mockError = new TestErrorInfo(code: ErrorResponseCode.BadRequest, message: "Input is invalid.");
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        status: TestWorkflowStatus.Failed, 
        error: mockError);

    // ACT: Create UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Failed, actual: testRun.Status);
}

協助程式方法

下一節說明範例測試方法所使用的方法。 協助程式方法會出現在類別定義中的測試方法底下。

GetTestMockDefinition()

下列方法會從 JSON 檔案載入模擬定義。 如果您的模擬數據以不同的位置或格式儲存,您可以編輯此方法。

private TestMockDefinition GetTestMockDefinition()
{
    var mockDataPath = Path.Combine(TestExecutor.rootDirectory, "Tests", TestExecutor.logicAppName, 
        TestExecutor.workflow, "<unit-test-name>", "<unit-test-name>-mock.json");
    return JsonConvert.DeserializeObject<TestMockDefinition>(File.ReadAllText(mockDataPath));
}

回撥方法

下列方法會動態產生模擬數據。 方法名稱會根據靜態或動態模擬數據測試方法中的模擬動作名稱而有所不同。 您可以編輯此方法,根據測試案例需求傳回不同的模擬回應,或使用它作為範本來建立您自己的動態回呼方法。

public CallExternalAPIActionMock CallExternalAPIActionMockOutputCallback(TestExecutionContext context)
{
    // Sample mock data: Dynamically change the mocked data for "actionName".
    return new CallExternalAPIActionMock(
        status: TestWorkflowStatus.Succeeded,
        outputs: new CallExternalAPIActionOutput {

            // If this account contains a JObject Body, 
            // set the properties you want here:
            // Body = "something".ToJObject()

        }
    );
}