簡體   English   中英

單元測試 AWS:步進函數

[英]Unit testing AWS: step function

我是 AWS 的初學者,我創建了下面給出的第一個 AWS 步驟函數,現在下一步是對這個步驟函數進行單元測試。 我獨立地對我的 lambda 函數進行了單元測試,現在我卡住了,不知道如何進行 step 函數的單元測試。

我也有一個問題,是否值得對 step 函數進行單元測試,有一段時間感覺是否可以完成,因為它只是一個 json。

我試圖搜索,但我在互聯網或 AWS 文檔上沒有任何線索任何有關此或任何示例用例的博客都將不勝感激 謝謝

{
"Comment": "An example of the Amazon States Language using a choice state.",
"StartAt": "LoginState",
States": {
"LoginState": {
  "Type": "Task",
  "Resource": "arn:aws:lambda:us-east-1:170344690019:function:myfirstLogin",
  "Next": "ChoiceState"
},
"ChoiceState": {
  "Type" : "Choice",
  "Choices": [
    {
      "Variable": "$.success",
      "BooleanEquals": true,
      "Next": "logoutState"
    },
    {
      "Variable": "$.success",
      "BooleanEquals": false,
      "Next": "DefaultState"
    }
  ],
  "Default": "DefaultState"
},

"logoutState": {
  "Type" : "Task",
  "Resource": "arn:aws:lambda:us-east-1:170344690019:function:myFirstLogout",
  "End": true
},


"DefaultState": {
  "Type": "Fail",
  "Error": "DefaultStateError",
  "Cause": "No Matches!"
}

}
}

這有點挑剔,但它會通知以下解釋。 在測試狀態機時,您正在將單元測試范圍擴展到集成測試。

那么為什么要挑剔呢? 由於您正在進入集成測試,您將需要能夠運行狀態機,以便您可以為其提供輸入並驗證輸出。 這里有兩種方法可以自動測試狀態機......

  1. 將您的狀態機部署到您的 AWS 賬戶中的測試環境中,並使用 AWS 提供的任何工具(cliboto3等)直接調用它。 這更接近於自動化測試,因為它在真實環境中測試狀態機。 如果您將其設置為 CI 管道的一部分,則需要您配置構建服務器,使其具有在您的 AWS 賬戶中安裝和執行狀態機所需的訪問權限。

  2. 嘗試使用stepfunctions-local 之類的方法來模擬本地系統或測試環境中的運行狀態機。 如果您有一個已經在運行現有單元測試的 CI 管道設置,則此選項可能很有用。 這將需要一些努力才能將工具正確安裝到您的 CI 環境中,但可能是值得的。

  3. 我個人最喜歡...使用localstack 這些人在模擬可以在 Docker 容器中啟動和運行的多個 AWS 服務方面做得非常出色。 如果您的 lambda 使用其他 AWS 服務,這將特別有用。 我喜歡在我的 CI 環境中運行它進行集成測試。

  4. 使用AWS SAM CLI 我自己沒用過這么多。 它要求您使用無服務器應用程序模型。 他們的文檔自從得到更多官方支持以來確實得到了改進,因此按照他們的指南和大量示例應該很容易使用。 在 CI 環境中運行它需要您在測試環境中安裝該工具。

我希望這有幫助。 我認為在此答案中共享任何代碼都無濟於事,因為您嘗試做的事情並非微不足道,並且可以通過多種方式實現。 例如,像 CircleCI 這樣的 CI 服務利用 Docker 容器,讓您可以選擇生成自己的 Docker 容器來運行 stepfunctions-local 或 localstack。

編輯

請參閱下面來自@niqui的答案 我相信我肯定會喜歡在 CI 環境中測試這個選項作為 stepfunctions-local 或 localstack 的替代方案,因為它是由 AWS 提供和維護的。

AWS 最近發布了Step Functions的可下載版本

為了在與 StepFunctions Local 交互期間模擬 Lambda 函數,一個解決方案是在測試設置時啟動的 Python 線程中創建一個假的 Lambda HTTP 服務,並使該服務能夠解析 HTTP 請求 URL 以確定要調用哪個函數。

我已經將此概念實現為 pytest 夾具: https : //github.com/chehsunliu/pytest-stepfunctions

用法

假設有一個狀態機,它只是收集所有 EMR 集群 ID,我們想在本地對其進行測試。

狀態機定義

{
  "StartAt": "ListIds",
  "States": {
    "ListIds": {
      "Type": "Task",
      "Resource": "${ListIdsLambdaArn}",
      "ResultPath": "$.cluster_ids",
      "End": true
    }
  }
}

拉姆達代碼

my/pkg/emr.py

import boto3


def list_ids(*args, **kwargs):
    emr_client = boto3.client("emr")
    response = emr_client.list_clusters()

    return [item["Id"] for item in response["Clusters"]]

測試代碼

tests/test_foo.py

import json
import time
from string import Template

import boto3
from botocore.stub import Stubber


def test_bar(aws_stepfunctions_endpoint_url):
    # Create the definition string.

    definition_template = Template("""
    {
      "StartAt": "ListIds",
      "States": {
        "ListIds": {
          "Type": "Task",
          "Resource": "${ListIdsLambdaArn}",
          "ResultPath": "$.cluster_ids",
          "End": true
        }
      }
    }
    """)
    list_ids_lambda_arn = "arn:aws:lambda:us-east-1:123456789012:function:my.pkg.emr.list_ids"
    definition = definition_template.safe_substitute(ListIdsLambdaArn=list_ids_lambda_arn)

    # Create the state machine resource.

    sfn_client = boto3.client("stepfunctions", endpoint_url=aws_stepfunctions_endpoint_url)
    state_machine_arn = sfn_client.create_state_machine(
        name="list-ids", definition=definition, roleArn="arn:aws:iam::012345678901:role/DummyRole"
    )["stateMachineArn"]

    # Mock the Lambda code.

    emr_client = boto3.client("emr")
    mocker.patch("my.pkg.emr.boto3", autospec=True).client.return_value = emr_client

    stubber = Stubber(emr_client)
    stubber.add_response(
        "list_clusters", service_response={"Clusters": [{"Id": "j-00001"}, {"Id": "j-00002"}]}
    )

    # Start and wait until the execution finishes.

    execution_arn = sfn_client.start_execution(
        stateMachineArn=state_machine_arn, name="list-ids-exec", input="{}"
    )["executionArn"]

    with stubber:
        while True:
            response = sfn_client.describe_execution(executionArn=execution_arn)
            if response["status"] != "RUNNING":
                break
            time.sleep(0.5)

    # Validate the results.

    stubber.assert_no_pending_responses()
    assert "SUCCEEDED" == response["status"]
    assert ["j-00001", "j-00002"] == json.loads(response["output"])["cluster_ids"]

運行測試

安裝依賴項:

$ pip install boto3 pytest pytest-stepfunctions pytest-mock

此處下載 StepFunctions Local JAR 並執行它:

$ java -jar /path/to/StepFunctionsLocal.jar \
    --lambda-endpoint http://localhost:13000 \
    --step-functions-endpoint http://localhost:8083 \
    --wait-time-scale 0

運行測試:

$ python -m pytest -v \
    --pytest-stepfunctions-endpoint-url=http://0.0.0.0:8083 \
    --pytest-stepfunctions-lambda-address=0.0.0.0 \
    --pytest-stepfunctions-lambda-port=13000 \
    ./tests

測試也可以在 Docker Compose 中進行,使用和維護要容易得多。 您可以在我的 repo 中查看自述文件。 希望這個裝置可以幫助發現這篇文章的人。

我遇到了類似的問題,所以我為 step functions編寫了一個AWS 單元測試器 它通過使用官方提供的 docker 鏡像來工作。

安裝:

yarn add step-functions-tester
yarn add mocha chai
const TestRunner = require('step-functions-tester')
const { expect } = require('chai')
let testRunner
describe('Step function tester', function () {
  this.timeout('30s')

  before('Set up test runner', async function () {
    testRunner = new TestRunner()
    await testRunner.setUp()
  })
  afterEach('Clean up', async function () {
    await testRunner.cleanUp()
  })
  after('Tear down', async function () {
    await testRunner.tearDown()
  })

  it('Step function test', async function () {
    // AWS Step Function definition
    const stepFunctionDefinition = {StartAt: 'FirstStep', States: {FirstStep: { /* ... */}}}

    const stepFunctionInput = {}

    // Keys are function names in the step function definition, values are arrays of calls
    const callStubs = {'arn:eu-west:111:mockLambda': [{result: 'First call result'}, {result: 'Second call result'}], /*... */}
    
    const { executions } = await testRunner.run(callStubs, stepFunctionDefinition, stepFunctionInput)
    expect(executions).deep.equal(expectedExecutions)
  })
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM