AWS Step Function Localでステートマシンを分離環境でテストする(Mock Responseを使う場合)

AWS Step Function Localでステートマシンを分離環境でテストする(Mock Responseを使う場合)

Clock Icon2022.07.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

前回の下記エントリでは、AWS Step Function LocalをMock Responseを使わずに試してみました。

今回は、AWS Step Function LocalでMock Responseを使った場合のテストを試してみました。

やってみた

準備

テスト対象のState MachineのASL(Amazon States Language)定義のファイルです。コンテナ上にState Machineを作成する際に読み込ませます。

{
  "Comment": "A description of my state machine",
  "StartAt": "GetParameter",
  "States": {
    "GetParameter": {
      "Type": "Task",
      "Next": "Choice",
      "Parameters": {
        "Name": "hoge"
      },
      "Resource": "arn:aws:states:::aws-sdk:ssm:getParameter",
      "ResultSelector": {
        "hoge.$": "$.Parameter.Value"
      }
    },
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.hoge",
          "StringMatches": "fuga",
          "Next": "FugaPath"
        }
      ],
      "Default": "NotFugaPath"
    },
    "FugaPath": {
      "Type": "Pass",
      "End": true
    },
    "NotFugaPath": {
      "Type": "Pass",
      "End": true
    }
  }
}

AWS Systems Manager Parameter StoreからGet:Parameterで値を取得し、値の文字列により分岐を行うChoice Stateがあります。

Mock ResponseのConfigです。これによりAWS ServiceからTaskへのレスポンスをMockすることができます。またレスポンスは複数のパターンを記述し、テスト実行時に使用することが可能です。

{
  "StateMachines": {
    "MyStateMachine": {
      "TestCases": {
        "FugaPathTest": {
          "GetParameter": "GetParameterFugaMockedSuccess"
        },
        "NotFugaPathTest": {
          "GetParameter": "GetParameterNotFugaMockedSuccess"
        }
      }
    }
  },
  "MockedResponses": {
    "GetParameterFugaMockedSuccess": {
      "0": {
        "Return": {
          "Parameter": {
            "Arn": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/hoge",
            "DataType": "text",
            "LastModifiedDate": "2022-07-26T05:38:43.052Z",
            "Name": "hoge",
            "Type": "String",
            "Value": "fuga",
            "Version": 4
          }
        }
      }
    },
    "GetParameterNotFugaMockedSuccess": {
      "0": {
        "Return": {
          "Parameter": {
            "Arn": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/hoge",
            "DataType": "text",
            "LastModifiedDate": "2022-07-26T05:38:43.052Z",
            "Name": "hoge",
            "Type": "String",
            "Value": "nyao",
            "Version": 4
          }
        }
      }
    }
  }
}

AWS Step Function LocalのDockerイメージamazon/aws-stepfunctions-localをPullします。

docker pull amazon/aws-stepfunctions-local

Pullしたイメージからコンテナを実行します。

docker run -p 8083:8083 \
  --mount type=bind,readonly,source=$(pwd)/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \
  -e SFN_MOCK_CONFIG="/home/StepFunctionsLocal/MockConfigFile.json" \
  amazon/aws-stepfunctions-local

create-state-machineコマンドを実行してコンテナ上にState Machineを作成します。

stateMachineName=MyStateMachine
aws stepfunctions create-state-machine \
  --endpoint-url http://localhost:8083 \
  --definition file://$(pwd)/MyStateMachine.asl.json \
  --name $stateMachineName \
  --role-arn "arn:aws:iam::123456789012:role/DummyRole"

テスト実行

成功パターン1(FugaPathTest)

start-executionコマンドでStateMachineを実行します。この時State Machineの指定の末尾で、MockConfigFile.jsonで定義したテストケースを#{TestCase}のように指定します。まずはFugaPathTestです。

executionName=$(date +"%s")
aws stepfunctions start-execution \
  --endpoint http://localhost:8083 \
  --name $executionName \
  --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:${stateMachineName}#FugaPathTest

get-execution-historyコマンドで実行履歴を取得します。

$ aws stepfunctions get-execution-history \
  --endpoint http://localhost:8083 \
  --execution-arn arn:aws:states:us-east-1:123456789012:execution:${stateMachineName}:${executionName}
{
    "events": [
        {
            "timestamp": "2022-07-26T15:08:27.554000+09:00",
            "type": "ExecutionStarted",
            "id": 1,
            "previousEventId": 0,
            "executionStartedEventDetails": {
                "input": "{}",
                "inputDetails": {
                    "truncated": false
                },
                "roleArn": "arn:aws:iam::123456789012:role/DummyRole"
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.555000+09:00",
            "type": "TaskStateEntered",
            "id": 2,
            "previousEventId": 0,
            "stateEnteredEventDetails": {
                "name": "GetParameter",
                "input": "{}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.561000+09:00",
            "type": "TaskScheduled",
            "id": 3,
            "previousEventId": 2,
            "taskScheduledEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter",
                "region": "us-east-1",
                "parameters": "{\"Name\":\"hoge\"}"
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.562000+09:00",
            "type": "TaskStarted",
            "id": 4,
            "previousEventId": 3,
            "taskStartedEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter"
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.626000+09:00",
            "type": "TaskSucceeded",
            "id": 5,
            "previousEventId": 4,
            "taskSucceededEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter",
                "output": "{\"hoge\":\"fuga\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.627000+09:00",
            "type": "TaskStateExited",
            "id": 6,
            "previousEventId": 5,
            "stateExitedEventDetails": {
                "name": "GetParameter",
                "output": "{\"hoge\":\"fuga\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.629000+09:00",
            "type": "ChoiceStateEntered",
            "id": 7,
            "previousEventId": 6,
            "stateEnteredEventDetails": {
                "name": "Choice",
                "input": "{\"hoge\":\"fuga\"}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.635000+09:00",
            "type": "ChoiceStateExited",
            "id": 8,
            "previousEventId": 7,
            "stateExitedEventDetails": {
                "name": "Choice",
                "output": "{\"hoge\":\"fuga\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.635000+09:00",
            "type": "PassStateEntered",
            "id": 9,
            "previousEventId": 8,
            "stateEnteredEventDetails": {
                "name": "FugaPath",
                "input": "{\"hoge\":\"fuga\"}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.637000+09:00",
            "type": "PassStateExited",
            "id": 10,
            "previousEventId": 9,
            "stateExitedEventDetails": {
                "name": "FugaPath",
                "output": "{\"hoge\":\"fuga\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:08:27.639000+09:00",
            "type": "ExecutionSucceeded",
            "id": 11,
            "previousEventId": 10,
            "executionSucceededEventDetails": {
                "output": "{\"hoge\":\"fuga\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        }
    ]
}

State Machine実行がFugaPathを通り成功したことが確認できました。

成功パターン2(NotFugaPathTest)

次にNotFugaPathTestのパターンです。

executionName=$(date +"%s")
aws stepfunctions start-execution \
  --endpoint http://localhost:8083 \
  --name $executionName \
  --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:${stateMachineName}#NotFugaPathTest

get-execution-historyコマンドで実行履歴を取得します。

$ aws stepfunctions get-execution-history \
  --endpoint http://localhost:8083 \
  --execution-arn arn:aws:states:us-east-1:123456789012:execution:${stateMachineName}:${executionName}
{
    "events": [
        {
            "timestamp": "2022-07-26T15:17:55.153000+09:00",
            "type": "ExecutionStarted",
            "id": 1,
            "previousEventId": 0,
            "executionStartedEventDetails": {
                "input": "{}",
                "inputDetails": {
                    "truncated": false
                },
                "roleArn": "arn:aws:iam::123456789012:role/DummyRole"
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.153000+09:00",
            "type": "TaskStateEntered",
            "id": 2,
            "previousEventId": 0,
            "stateEnteredEventDetails": {
                "name": "GetParameter",
                "input": "{}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.154000+09:00",
            "type": "TaskScheduled",
            "id": 3,
            "previousEventId": 2,
            "taskScheduledEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter",
                "region": "us-east-1",
                "parameters": "{\"Name\":\"hoge\"}"
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.154000+09:00",
            "type": "TaskStarted",
            "id": 4,
            "previousEventId": 3,
            "taskStartedEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter"
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.156000+09:00",
            "type": "TaskSucceeded",
            "id": 5,
            "previousEventId": 4,
            "taskSucceededEventDetails": {
                "resourceType": "aws-sdk",
                "resource": "ssm:getParameter",
                "output": "{\"hoge\":\"nyao\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.156000+09:00",
            "type": "TaskStateExited",
            "id": 6,
            "previousEventId": 5,
            "stateExitedEventDetails": {
                "name": "GetParameter",
                "output": "{\"hoge\":\"nyao\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.156000+09:00",
            "type": "ChoiceStateEntered",
            "id": 7,
            "previousEventId": 6,
            "stateEnteredEventDetails": {
                "name": "Choice",
                "input": "{\"hoge\":\"nyao\"}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.157000+09:00",
            "type": "ChoiceStateExited",
            "id": 8,
            "previousEventId": 7,
            "stateExitedEventDetails": {
                "name": "Choice",
                "output": "{\"hoge\":\"nyao\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.158000+09:00",
            "type": "PassStateEntered",
            "id": 9,
            "previousEventId": 8,
            "stateEnteredEventDetails": {
                "name": "NotFugaPath",
                "input": "{\"hoge\":\"nyao\"}",
                "inputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.158000+09:00",
            "type": "PassStateExited",
            "id": 10,
            "previousEventId": 9,
            "stateExitedEventDetails": {
                "name": "NotFugaPath",
                "output": "{\"hoge\":\"nyao\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        },
        {
            "timestamp": "2022-07-26T15:17:55.158000+09:00",
            "type": "ExecutionSucceeded",
            "id": 11,
            "previousEventId": 10,
            "executionSucceededEventDetails": {
                "output": "{\"hoge\":\"nyao\"}",
                "outputDetails": {
                    "truncated": false
                }
            }
        }
    ]
}

State Machine実行がNotFugaPathを通り成功したことが確認できました。

おわりに

AWS Step Function LocalでMock Responseを使った場合のテストを試してみました。

State Machineのテストをしたい場合、今までは実環境を使用する他なかったのですが、その際にAWS Serviceからテストケースで期待したレスポンスを行うようにするのがとても大変で、また限界がありました。AWS Step Function Localを使えば柔軟かつ簡単にテストケースを設定できそうです。

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.