SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

AWS Step Functions Local モック統合機能を利用したローカルテスト

こんにちは、NRIデジタルの島です。

先日、AWS Step Functions(以下 Step Functions)を使ったバッチジョブワークフローの実行基盤構築の記事を公開致しました(AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digital)。

Step Functionsは上記記事で記載した通り、アプリケーションの一連のワークフローを構築・管理するマネージドなサーバレスワークフローサービスで、様々なAWSサービスと連携したワークフローを容易に構築できます。
非常に便利なサービスですが、Step Functionsで構築したワークフローをテストしたい場合、連携先の各サービスが正常に稼働していなければいけません。開発の流れを考えると基盤レイヤであるワークフローは各サービスが作られる前に、事前に確認したい場面もあるかと思います。

2019年2月にローカル環境でStep Functionsをテスト出来る「Step Functions Local」が利用できるようになりました(AWS Step Functions ワークフローをローカルで開発してテストする)。

本記事では、Step Functions Localを使用して、上記記事で記載したワークフローのローカルテストを実施してみた結果を共有させていただきたいと思います。

Step Functions Local

Step Functions Localはローカル環境でStep Functionsのワークフローをシミュレーション出来るツールです。
Javaで実装されており、JarかDockerコンテナ実行で使用可能です。

Jar利用

Step Functions Local (ダウンロード可能バージョン) のセットアップ-Java バージョン – AWS Step Functions

Dockerイメージ利用

Step Functions Local (ダウンロード可能なバージョン) と Docker のセットアップ – AWS Step Functions

Step Functions Local自体はStep Functionsをローカルで実行できるものであり、呼び出すサービスは実際のAWS環境で立ち上げるか、下記公式ページのようにローカルで必要なサービスを起動する必要があります。
Step Functions テストとAWS SAMCLI Local

しかしながら、前述の通りまだ連携先のサービスがまだ作られてない時点での事前テストを想定した場合、その為のモック的なサービスを自前で構築するのには手間がかかります。

このニーズを満たすものとして、先日Step Functions Localのモックサービス統合機能がリリースされました(Mocking service integrations with AWS Step Functions Local)。
※ここの冒頭で「Often, developers want to test the control and data flows of their state machine executions in isolation, without any dependency on service integration availability」と記載されている通り、まさに現場のニーズを鑑みた機能追加であると読み取れます。

モックテスト検証

では、先日の記事に記載したワークフローをモックテストしてみたいと思います。
構成としては、親のステートマシンでジョブワークフロー(いわゆるジョブネット)を管理し、実際の処理は呼び出し先の子ステートマシンで実行されます。

AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digitalより抜粋

※上図のJOB1は同期、JOB2以降は非同期呼び出しとなります
※この構成を採用・検証した背景などは当該記事をご参照ください
(再掲 AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digital

なお、執筆時点では複数ステートマシンを全てつなげたモックテストは実行出来ません。 あくまで個々のステートマシン単位でのテストとなりますのでご注意ください。

では、本構成の非同期呼び出しの子ステートマシン単体(job2に該当する下図赤枠のステートマシン)のモックテストを実施してみます。

1. ステートマシン定義

まず、テストしたいステートマシンのワークフロー定義ファイルを作成します。
※執筆時点では、以下の通りステートマシン定義はJSONのみ対応しています
create-state-machine – AWS CLI 2.7.2 Command Reference

Job02-asl.json
{
  "StartAt": "GetStatus",
  "States": {
    "GetStatus": {
      "Type": "Task",
      "Resource": "arn:aws:states:::dynamodb:getItem",
      "Parameters": {
        "TableName": "ddbt-coe-sfn-stg0-status-mng",
        "Key": {
          "id": {
            "S.$": "$.frame_id"
          },
          "job_id": {
            "S.$": "$.job_id"
          }
        }
      },
      "InputPath": "$",
      "ResultPath": "$.DynamoDB",
      "Next": "ChoiceState"
    },
    "ChoiceState": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.DynamoDB.Item",
          "IsPresent": false,
          "Next": "GenerateRandomIntValue"
        }
      ],
      "Default": "TaskTokenHandler",
      "InputPath": "$"
    },
    "GenerateRandomIntValue": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException"
          ],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 1.0
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "TaskFailureHandler"
        }
      ],
      "InputPath": "$",
      "ResultPath": "$.intValue",
      "Next": "RegisterResult"
    },
    "RegisterResult": {
      "Type": "Task",
      "Resource": "arn:aws:states:::dynamodb:putItem",
      "Parameters": {
        "TableName": "ddbt-coe-sfn-stg0-status-mng",
        "Item": {
          "id": {
            "S.$": "$.frame_id"
          },
          "job_id.$": "$.job_id",
          "status": "0"
        }
      },
      "InputPath": "$",
      "ResultPath": "$.DynamoDB",
      "Next": "TaskTokenHandler"
    },
    "TaskTokenHandler": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
      "Parameters": {
        "taskToken.$": "$.TaskToken",
        "err": "false"
      },
      "InputPath": "$",
      "ResultPath": "$.result",
      "Next": "PassToEnd"
    },
    "TaskFailureHandler": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
      "Parameters": {
        "taskToken.$": "$.TaskToken",
        "err": "true"
      },
      "InputPath": "$",
      "ResultPath": "$.result",
      "Next": "Failed"
    },
    "PassToEnd": {
      "Type": "Pass",
      "End": true
    },
    "Failed": {
      "Type": "Fail",
      "Error": "States.TaskFailed"
    }
  }
}

上記ワークフローの流れは以下の通りです。

  1. GetStatusステート
  2. 親ステートマシンから渡されるFrameId(バッチの一連のシーケンス実行単位を表すID)とJobIdをキーにDynamoDBから実行済みのレコードを取得する。
    取得後、②のステートへ遷移。

  3. ChoiceStateステート
  4. ①の取得でレコードが存在していれば「実行済み」と判断し、⑤のステートへ遷移(③のステートをスキップ)。
    レコードが存在していなければ「未実行」と判断し、③のステートへ遷移。

  5. GenerateRandomValueステート
  6. ランダム値を返すだけの単純なLambda関数をコールする。
    (アプリケーションの処理の内容に意味はありません)
    処理が成功した場合は④のステートへ、失敗した場合は⑥のステートへ遷移。

  7. RegisterResultステート
  8. DynamoDBに実行済みレコードを登録する。
    登録後⑤のステートへ遷移。

  9. TaskTokenHandlerステート
  10. 親ステートマシンへ正常終了のコールバックを送信するLambda関数をコールする。
    コールバック後、⑦のステートへ遷移。

  11. TaskFailureHandlerステート
  12. 親ステートマシンへ異常終了のコールバックを送信するLambda関数をコールする。
    コールバック後、当該フローを異常終了とさせる為、⑧のステートへ遷移。

  13. PassToEndステート
  14. End(正常終了)へパスするステート。
    Type=Choiceのステート(②ChoiceState)から直接Endへ遷移出来ない仕様の為、正常終了時は共通的に本ステートを通す。

  15. Failedステート
  16. 当該フローを異常終了とさせる。

    2. モック設定ファイル

    次にモック設定ファイルを作成します。
    設定方法や各種フィールドのリファレンスは以下をご参照ください。
    モックサービス統合の設定ファイル – AWS Step Functions

    設定は2つのレイヤで構成されています。

    StateMachines

    モックサービス統合を使用するステートマシンの定義。
    「TestCases」フィールドがそのステートマシンの個々のテストケースを表す。

    MockedResponses

    テストケースから呼ばれるモックレスポンスの設定。

    今回は以下のように設定致しました。

    MockConfigFile.json

    {
      "StateMachines": {
        "coe-job02-statemachine": {
          "TestCases": {
            "NormalPath": {
              "GetStatus": "MockedGetNoData",
              "GenerateRandomIntValue": "MockedMessages",
              "RegisterResult": "MockedResisterMessages",
              "TaskTokenHandler": "MockedHttpNormalResponse"
            },
            "SkipPath": {
              "GetStatus": "MockedGetData",
              "TaskTokenHandler": "MockedHttpNormalResponse"
            },
            "AnomalyPath": {
              "GetStatus": "MockedGetNoData",
              "GenerateRandomIntValue": "MockedMessagesErrorStatus",
              "TaskFailureHandler": "MockedHttpNormalResponse",
              "RegisterResult": "MockedResisterMessages",
              "TaskTokenHandler": "MockedHttpNormalResponse"
            }
          }
        }
      },
      "MockedResponses": {
        "MockedGetData": {
          "0": {
            "Return": {
              "Item": "123456789"
            }
          }
        },
        "MockedGetNoData": {
          "0": {
            "Return": {}
          }
        },
        "MockedMessages": {
          "0": {
            "Return": {
              "intValue": "123"
            }
          }
        },
        "MockedResisterMessages": {
          "0": {
            "Return": {
              "StatusCode": 200,
              "Payload": {
                "StatusCode": 200,
                "body": "OK"
              }
            }
          }
        },
        "MockedMessagesErrorStatus": {
          "0-999": {
            "Throw": {
              "Error": "Lambda.ServiceException",
              "Cause": "exception occur."
            }
          }
        }
      }
    }
    

    今回試したテストケースは以下3ケースとなります。

    NormalPath

    ジョブが未実行の正常系ケースで、初回実行時を想定。

    SkipPath

    既にジョブが実行済みの正常系ケースで、再実行時を想定。

    AnomalyPath

    「③GenerateRandomValueステート」が呼び出すLambda関数がエラーとなる異常系ケースで、リトライ試行回数分全てエラーとなる状況を想定。

    3. Step Functions Localの起動

    Step Functions Localを起動します。
    前述した通り、Jar実行かDockerコンテナ実行かを選択できますが、今回はJar実行にて試してみます。

    # モック設定ファイルの場所を環境変数に指定
    export SFN_MOCK_CONFIG="[PATH_TO_MOCKFILE]/MockConfigFile.json"
    
    # リージョン指定
    export AWS_DEFAULT_REGION=ap-northeast-1
     
    # 起動
    java -jar StepFunctionsLocal.jar
    

    4. ステートマシンの登録

    「1. ステートマシン定義」で定義したテスト対象のステートマシンを登録します。

    # ステートマシンの登録 ※ロールはダミーでOK
    aws stepfunctions create-state-machine --endpoint http://localhost:8083 --definition file://statemachine/Job02-asl.json --name "coe-job02-statemachine" --role-arn "arn:aws:iam::123456789012:role/service-role/Dummy"
    
    # ログ
    2022-03-31 18:24:57.777: [200] CreateStateMachine <= {"sdkResponseMetadata":null,"sdkHttpMetadata":null,"stateMachineArn":"arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine","creationDate":1648718697763}
    
    

    5. ローカルテストの実行

    準備が整いましたので、テスト実行します。

    シナリオ1 (NormalPath)

    NormalPathを実行します。

    # NormalPath実行
    aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#NormalPath --input "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}"
    
    {
        "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220331-182725",
        "startDate": "2022-03-31T18:27:26.725000+09:00"
    }
    

    実行時のログを確認します。
    「1. ステートマシン定義」で記載したワークフローが「①→②→③→④→⑤→⑦」と実行されるのが想定結果です。
    結果の確認は、実行しているStep Functions Localのコンソールログか、「get-execution-history」コマンドで確認できます。

    本検証では「get-execution-history」コマンドで確認します。
    get-execution-history – AWS CLI 1.25.1 Command Reference

    各ステート(主にType=Task)の各イベントは正常系の場合、
    「Entered→Scheduled→Started→Succeeded→Exited」となります。また、各イベントの「id及びpreviousEventId」を確認することで、各ステート、イベントの流れをトレースすることが出来ます。

    以下の通り、NormalPathシナリオで、想定の動作をしていることが確認できました。

    # get-execution-history
    aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220331-182725
    {
        "events": [
            {
                "timestamp": "2022-03-31T18:27:26.742000+09:00",
                "type": "ExecutionStarted",
                "id": 1,
                "previousEventId": 0,
                "executionStartedEventDetails": {
                    "input": "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    },
                    "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy"
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.743000+09:00",
                "type": "TaskStateEntered",
                "id": 2,
                "previousEventId": 0,
                "stateEnteredEventDetails": {
                    "name": "GetStatus",
                    "input": "{\"frame_id\" : \"20220331-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.759000+09:00",
                "type": "TaskScheduled",
                "id": 3,
                "previousEventId": 2,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220331-001\"},\"job_id\":{\"S\":\"job2_1\"}}}"
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.760000+09:00",
                "type": "TaskStarted",
                "id": 4,
                "previousEventId": 3,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem"
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.777000+09:00",
                "type": "TaskSucceeded",
                "id": 5,
                "previousEventId": 4,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.779000+09:00",
                "type": "TaskStateExited",
                "id": 6,
                "previousEventId": 5,
                "stateExitedEventDetails": {
                    "name": "GetStatus",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.782000+09:00",
                "type": "ChoiceStateEntered",
                "id": 7,
                "previousEventId": 6,
                "stateEnteredEventDetails": {
                    "name": "ChoiceState",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.785000+09:00",
                "type": "ChoiceStateExited",
                "id": 8,
                "previousEventId": 7,
                "stateExitedEventDetails": {
                    "name": "ChoiceState",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.786000+09:00",
                "type": "TaskStateEntered",
                "id": 9,
                "previousEventId": 8,
                "stateEnteredEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.788000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 10,
                "previousEventId": 9,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.788000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 11,
                "previousEventId": 10
            },
            {
                "timestamp": "2022-03-31T18:27:26.791000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 12,
                "previousEventId": 11,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"intValue\":\"123\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.792000+09:00",
                "type": "TaskStateExited",
                "id": 13,
                "previousEventId": 12,
                "stateExitedEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"intValue\":\"123\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.793000+09:00",
                "type": "TaskStateEntered",
                "id": 14,
                "previousEventId": 13,
                "stateEnteredEventDetails": {
                    "name": "RegisterResult",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"intValue\":\"123\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.793000+09:00",
                "type": "TaskScheduled",
                "id": 15,
                "previousEventId": 14,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Item\":{\"id\":{\"S\":\"20220331-001\"},\"status\":\"0\",\"job_id\":\"job2_1\"}}"
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.802000+09:00",
                "type": "TaskStarted",
                "id": 16,
                "previousEventId": 15,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem"
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.803000+09:00",
                "type": "TaskSucceeded",
                "id": 17,
                "previousEventId": 16,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.803000+09:00",
                "type": "TaskStateExited",
                "id": 18,
                "previousEventId": 17,
                "stateExitedEventDetails": {
                    "name": "RegisterResult",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.804000+09:00",
                "type": "TaskStateEntered",
                "id": 19,
                "previousEventId": 18,
                "stateEnteredEventDetails": {
                    "name": "TaskTokenHandler",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.805000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 20,
                "previousEventId": 19,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
                    "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.806000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 21,
                "previousEventId": 20
            },
            {
                "timestamp": "2022-03-31T18:27:26.806000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 22,
                "previousEventId": 21,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"StatusCode\":200,\"body\":\"OK\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.807000+09:00",
                "type": "TaskStateExited",
                "id": 23,
                "previousEventId": 22,
                "stateExitedEventDetails": {
                    "name": "TaskTokenHandler",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.807000+09:00",
                "type": "PassStateEntered",
                "id": 24,
                "previousEventId": 23,
                "stateEnteredEventDetails": {
                    "name": "PassToEnd",
                    "input": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.808000+09:00",
                "type": "PassStateExited",
                "id": 25,
                "previousEventId": 24,
                "stateExitedEventDetails": {
                    "name": "PassToEnd",
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-03-31T18:27:26.810000+09:00",
                "type": "ExecutionSucceeded",
                "id": 26,
                "previousEventId": 25,
                "executionSucceededEventDetails": {
                    "output": "{\"frame_id\":\"20220331-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"intValue\":\"123\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            }
        ]
    }
    
    
    シナリオ2(SkipPath)

    SkipPathを実行します。

    # SkipPath実行
    aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#SkipPath --input "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}"
    {
        "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-124831",
        "startDate": "2022-04-01T12:48:32.874000+09:00"
    }
    

    実行時のログを確認します。
    「1. ステートマシン定義」で記載したワークフローが「①→②→⑤→⑦」と実行される(実行済みの為、③④はスキップ)のが想定結果です。

    以下の通り、想定通りの結果となっていることが確認できました。

    # get-execution-history
    aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-124831
    {
        "events": [
            {
                "timestamp": "2022-04-01T12:48:32.878000+09:00",
                "type": "ExecutionStarted",
                "id": 1,
                "previousEventId": 0,
                "executionStartedEventDetails": {
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    },
                    "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy"
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.880000+09:00",
                "type": "TaskStateEntered",
                "id": 2,
                "previousEventId": 0,
                "stateEnteredEventDetails": {
                    "name": "GetStatus",
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.890000+09:00",
                "type": "TaskScheduled",
                "id": 3,
                "previousEventId": 2,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}"
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.893000+09:00",
                "type": "TaskStarted",
                "id": 4,
                "previousEventId": 3,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem"
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.898000+09:00",
                "type": "TaskSucceeded",
                "id": 5,
                "previousEventId": 4,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.899000+09:00",
                "type": "TaskStateExited",
                "id": 6,
                "previousEventId": 5,
                "stateExitedEventDetails": {
                    "name": "GetStatus",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.899000+09:00",
                "type": "ChoiceStateEntered",
                "id": 7,
                "previousEventId": 6,
                "stateEnteredEventDetails": {
                    "name": "ChoiceState",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.904000+09:00",
                "type": "ChoiceStateExited",
                "id": 8,
                "previousEventId": 7,
                "stateExitedEventDetails": {
                    "name": "ChoiceState",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.904000+09:00",
                "type": "TaskStateEntered",
                "id": 9,
                "previousEventId": 8,
                "stateEnteredEventDetails": {
                    "name": "TaskTokenHandler",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.906000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 10,
                "previousEventId": 9,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
                    "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.906000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 11,
                "previousEventId": 10
            },
            {
                "timestamp": "2022-04-01T12:48:32.907000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 12,
                "previousEventId": 11,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"StatusCode\":200,\"body\":\"OK\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.908000+09:00",
                "type": "TaskStateExited",
                "id": 13,
                "previousEventId": 12,
                "stateExitedEventDetails": {
                    "name": "TaskTokenHandler",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.909000+09:00",
                "type": "PassStateEntered",
                "id": 14,
                "previousEventId": 13,
                "stateEnteredEventDetails": {
                    "name": "PassToEnd",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.909000+09:00",
                "type": "PassStateExited",
                "id": 15,
                "previousEventId": 14,
                "stateExitedEventDetails": {
                    "name": "PassToEnd",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T12:48:32.909000+09:00",
                "type": "ExecutionSucceeded",
                "id": 16,
                "previousEventId": 15,
                "executionSucceededEventDetails": {
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"Item\":\"123456789\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            }
        ]
    }
    
    
    シナリオ3(AnomalyPath)

    AnomalyPathを実行します。

    # AnomalyPath実行
    aws stepfunctions start-execution --endpoint http://localhost:8083 --name coe-statemachine-`date +%Y%m%d-%H%M%S` --state-machine arn:aws:states:ap-northeast-1:123456789012:stateMachine:coe-job02-statemachine#AnomalyPath --input "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}"
    {
        "executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:coe-job02-statemachine:coe-statemachine-20220401-145907",
        "startDate": "2022-04-01T14:59:08.005000+09:00"
    }
    

    実行時のログを確認します。
    「1. ステートマシン定義」で記載したワークフローが「①→②→③→⑥→⑧」と実行される(異常終了となる)のが想定結果です。
    ポイントは、「1. ステートマシン定義」の「③GenerateRandomValueステート」がエラー時に最大「3回(5秒毎)」リトライしてるが、モックレスポンスは常にエラーを返却する為、「3回」リトライ後(計4回)に異常終了となる点です。

    GenerateRandomValueステート定義抜粋
    ※リトライ定義

    "Retry": [
      {
        "ErrorEquals": [
          "States.ALL"
        ],
        "IntervalSeconds": 5,
        "MaxAttempts": 3,
        "BackoffRate": 1.0
      }
    ],
    

    モック設定抜粋
    ※常にエラーを返す

    "MockedMessagesErrorStatus": {
      "0-999": {
        "Throw": {
          "Error": "Lambda.ServiceException",
          "Cause": "exception occur."
        }
      }
    },
    

    以下の通り、想定通りの結果となっていることが確認できました。
    ※GenerateRandomValueステートが4回試行された後、異常終了になっていることがわかります
    →4度の試行失敗は「140、169、198、227行目」に出力されております
    140行目: “timestamp”: “2022-04-01T14:59:08.054000+09:00”,
    169行目: “timestamp”: “2022-04-01T14:59:13.059000+09:00”,
    198行目: “timestamp”: “2022-04-01T14:59:18.075000+09:00”,
    227行目: “timestamp”: “2022-04-01T14:59:23.084000+09:00”,

    # get-execution-history
    aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn arn:aws:states:ap-northeast-1:123456789012:execution
    :coe-job02-statemachine:coe-statemachine-20220401-124831
    {
        "events": [
            {
                "timestamp": "2022-04-01T14:59:08.018000+09:00",
                "type": "ExecutionStarted",
                "id": 1,
                "previousEventId": 0,
                "executionStartedEventDetails": {
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    },
                    "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy"
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.019000+09:00",
                "type": "TaskStateEntered",
                "id": 2,
                "previousEventId": 0,
                "stateEnteredEventDetails": {
                    "name": "GetStatus",
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.031000+09:00",
                "type": "TaskScheduled",
                "id": 3,
                "previousEventId": 2,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}"
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.032000+09:00",
                "type": "TaskStarted",
                "id": 4,
                "previousEventId": 3,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem"
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.043000+09:00",
                "type": "TaskSucceeded",
                "id": 5,
                "previousEventId": 4,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.044000+09:00",
                "type": "TaskStateExited",
                "id": 6,
                "previousEventId": 5,
                "stateExitedEventDetails": {
                    "name": "GetStatus",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.046000+09:00",
                "type": "ChoiceStateEntered",
                "id": 7,
                "previousEventId": 6,
                "stateEnteredEventDetails": {
                    "name": "ChoiceState",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.048000+09:00",
                "type": "ChoiceStateExited",
                "id": 8,
                "previousEventId": 7,
                "stateExitedEventDetails": {
                    "name": "ChoiceState",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.048000+09:00",
                "type": "TaskStateEntered",
                "id": 9,
                "previousEventId": 8,
                "stateEnteredEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.050000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 10,
                "previousEventId": 9,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:08.051000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 11,
                "previousEventId": 10
            },
            {
                "timestamp": "2022-04-01T14:59:08.054000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 12,
                "previousEventId": 11,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "exception occur."
                }
            },
            {
                "timestamp": "2022-04-01T14:59:13.057000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 13,
                "previousEventId": 12,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:13.058000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 14,
                "previousEventId": 13
            },
            {
                "timestamp": "2022-04-01T14:59:13.059000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 15,
                "previousEventId": 14,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "exception occur."
                }
            },
            {
                "timestamp": "2022-04-01T14:59:18.074000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 16,
                "previousEventId": 15,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:18.075000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 17,
                "previousEventId": 16
            },
            {
                "timestamp": "2022-04-01T14:59:18.075000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 18,
                "previousEventId": 17,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "exception occur."
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.082000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 19,
                "previousEventId": 18,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.082000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 20,
                "previousEventId": 19
            },
            {
                "timestamp": "2022-04-01T14:59:23.084000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 21,
                "previousEventId": 20,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "exception occur."
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.086000+09:00",
                "type": "TaskStateExited",
                "id": 22,
                "previousEventId": 18,
                "stateExitedEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.087000+09:00",
                "type": "TaskStateEntered",
                "id": 23,
                "previousEventId": 22,
                "stateEnteredEventDetails": {
                    "name": "TaskFailureHandler",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.088000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 24,
                "previousEventId": 23,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
                    "input": "{\"err\":\"true\",\"taskToken\":\"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.088000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 25,
                "previousEventId": 24
            },
            {
                "timestamp": "2022-04-01T14:59:23.090000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 26,
                "previousEventId": 25,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"StatusCode\":200,\"body\":\"OK\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.092000+09:00",
                "type": "TaskStateExited",
                "id": 27,
                "previousEventId": 26,
                "stateExitedEventDetails": {
                    "name": "TaskFailureHandler",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.095000+09:00",
                "type": "FailStateEntered",
                "id": 28,
                "previousEventId": 27,
                "stateEnteredEventDetails": {
                    "name": "Failed",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"error\":{\"Error\":\"Lambda.ServiceException\",\"Cause\":\"exception occur.\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T14:59:23.097000+09:00",
                "type": "ExecutionFailed",
                "id": 29,
                "previousEventId": 28,
                "executionFailedEventDetails": {
                    "error": "States.TaskFailed"
                }
            }
        ]
    }
    

    なお、モック設定を以下にすることで、リトライ試行内で正常レスポンスを返却でき、フローとして正常終了することも確認可能です。

    モック設定抜粋
    ※3回はエラー、4回目に正常を返す

    "MockedMessagesErrorStatus": {
          "0-2": {
            "Throw": {
              "Error": "RetryUntilSentStatusException",
              "Cause": "not ready."
            }
          },
          "3": {
            "Return": {
              "StatusCode": 200,
              "Payload": {
                "StatusCode": 200,
                "body": "OK"
              }
            }
          }
        },
    
    上記設定で実行すると、3回リトライ後正常終了することがわかります。
    {
        "events": [
            {
                "timestamp": "2022-04-01T16:09:36.496000+09:00",
                "type": "ExecutionStarted",
                "id": 1,
                "previousEventId": 0,
                "executionStartedEventDetails": {
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    },
                    "roleArn": "arn:aws:iam::123456789012:role/service-role/Dummy"
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.497000+09:00",
                "type": "TaskStateEntered",
                "id": 2,
                "previousEventId": 0,
                "stateEnteredEventDetails": {
                    "name": "GetStatus",
                    "input": "{\"frame_id\" : \"20220401-001\",\"job_id\" : \"job2_1\", \"TaskToken\" : \"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.507000+09:00",
                "type": "TaskScheduled",
                "id": 3,
                "previousEventId": 2,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Key\":{\"id\":{\"S\":\"20220401-001\"},\"job_id\":{\"S\":\"job2_1\"}}}"
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.508000+09:00",
                "type": "TaskStarted",
                "id": 4,
                "previousEventId": 3,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem"
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.521000+09:00",
                "type": "TaskSucceeded",
                "id": 5,
                "previousEventId": 4,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "getItem",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.522000+09:00",
                "type": "TaskStateExited",
                "id": 6,
                "previousEventId": 5,
                "stateExitedEventDetails": {
                    "name": "GetStatus",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.523000+09:00",
                "type": "ChoiceStateEntered",
                "id": 7,
                "previousEventId": 6,
                "stateEnteredEventDetails": {
                    "name": "ChoiceState",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.526000+09:00",
                "type": "ChoiceStateExited",
                "id": 8,
                "previousEventId": 7,
                "stateExitedEventDetails": {
                    "name": "ChoiceState",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.526000+09:00",
                "type": "TaskStateEntered",
                "id": 9,
                "previousEventId": 8,
                "stateEnteredEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.528000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 10,
                "previousEventId": 9,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:36.528000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 11,
                "previousEventId": 10
            },
            {
                "timestamp": "2022-04-01T16:09:36.532000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 12,
                "previousEventId": 11,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "not ready."
                }
            },
            {
                "timestamp": "2022-04-01T16:09:41.539000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 13,
                "previousEventId": 12,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:41.540000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 14,
                "previousEventId": 13
            },
            {
                "timestamp": "2022-04-01T16:09:41.540000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 15,
                "previousEventId": 14,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "not ready."
                }
            },
            {
                "timestamp": "2022-04-01T16:09:46.554000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 16,
                "previousEventId": 15,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:46.554000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 17,
                "previousEventId": 16
            },
            {
                "timestamp": "2022-04-01T16:09:46.555000+09:00",
                "type": "LambdaFunctionFailed",
                "id": 18,
                "previousEventId": 17,
                "lambdaFunctionFailedEventDetails": {
                    "error": "Lambda.ServiceException",
                    "cause": "not ready."
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.558000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 19,
                "previousEventId": 18,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-random-number-generator",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.559000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 20,
                "previousEventId": 19
            },
            {
                "timestamp": "2022-04-01T16:09:51.562000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 21,
                "previousEventId": 20,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"StatusCode\":200,\"body\":\"OK\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.563000+09:00",
                "type": "TaskStateExited",
                "id": 22,
                "previousEventId": 21,
                "stateExitedEventDetails": {
                    "name": "GenerateRandomIntValue",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.563000+09:00",
                "type": "TaskStateEntered",
                "id": 23,
                "previousEventId": 22,
                "stateEnteredEventDetails": {
                    "name": "RegisterResult",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.564000+09:00",
                "type": "TaskScheduled",
                "id": 24,
                "previousEventId": 23,
                "taskScheduledEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem",
                    "region": "ap-northeast-1",
                    "parameters": "{\"TableName\":\"ddbt-coe-sfn-stg0-status-mng\",\"Item\":{\"id\":{\"S\":\"20220401-001\"},\"status\":\"0\",\"job_id\":\"job2_1\"}}"
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.565000+09:00",
                "type": "TaskStarted",
                "id": 25,
                "previousEventId": 24,
                "taskStartedEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem"
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.565000+09:00",
                "type": "TaskSucceeded",
                "id": 26,
                "previousEventId": 25,
                "taskSucceededEventDetails": {
                    "resourceType": "dynamodb",
                    "resource": "putItem",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.566000+09:00",
                "type": "TaskStateExited",
                "id": 27,
                "previousEventId": 26,
                "stateExitedEventDetails": {
                    "name": "RegisterResult",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.566000+09:00",
                "type": "TaskStateEntered",
                "id": 28,
                "previousEventId": 27,
                "stateEnteredEventDetails": {
                    "name": "TaskTokenHandler",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.567000+09:00",
                "type": "LambdaFunctionScheduled",
                "id": 29,
                "previousEventId": 28,
                "lambdaFunctionScheduledEventDetails": {
                    "resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:lmdf-coe-sfn-stg0-task-token-handler",
                    "input": "{\"err\":\"false\",\"taskToken\":\"1234567890\"}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.568000+09:00",
                "type": "LambdaFunctionStarted",
                "id": 30,
                "previousEventId": 29
            },
            {
                "timestamp": "2022-04-01T16:09:51.568000+09:00",
                "type": "LambdaFunctionSucceeded",
                "id": 31,
                "previousEventId": 30,
                "lambdaFunctionSucceededEventDetails": {
                    "output": "{\"StatusCode\":200,\"body\":\"OK\"}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.571000+09:00",
                "type": "TaskStateExited",
                "id": 32,
                "previousEventId": 31,
                "stateExitedEventDetails": {
                    "name": "TaskTokenHandler",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.572000+09:00",
                "type": "PassStateEntered",
                "id": 33,
                "previousEventId": 32,
                "stateEnteredEventDetails": {
                    "name": "PassToEnd",
                    "input": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "inputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.573000+09:00",
                "type": "PassStateExited",
                "id": 34,
                "previousEventId": 33,
                "stateExitedEventDetails": {
                    "name": "PassToEnd",
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            },
            {
                "timestamp": "2022-04-01T16:09:51.575000+09:00",
                "type": "ExecutionSucceeded",
                "id": 35,
                "previousEventId": 34,
                "executionSucceededEventDetails": {
                    "output": "{\"frame_id\":\"20220401-001\",\"job_id\":\"job2_1\",\"TaskToken\":\"1234567890\",\"DynamoDB\":{\"StatusCode\":200,\"Payload\":{\"StatusCode\":200,\"body\":\"OK\"}},\"intValue\":{\"StatusCode\":200,\"body\":\"OK\"},\"result\":{\"StatusCode\":200,\"body\":\"OK\"}}",
                    "outputDetails": {
                        "truncated": false
                    }
                }
            }
        ]
    }
    
    

    以上、実際のサービスを立ち上げることなく、ステートマシン単体でのワークフローのローカルテストが実行できました。

    非同期連携部分のテスト

    本構成においては、親ステートマシンから子ステートマシンをEventBridge経由で非同期に呼び出すような方式を採用しております。

    AWS Step Functionsでバッチジョブワークフローの実行基盤を構築する|TECH|NRI digitalより抜粋

    本構成における親ステートマシンをStep Functions Localのモック統合でテスト可能かどうか確認しましたが、残念ながら執筆時点では非同期連携部分のテスト実施はできなそうです。
    非同期となる為、親ステートマシンのフロー制御は、コールバックがどのタイミングで実行されるかに依存します。モック機能にコールバックのタイミングを制御するような機能はない為、現時点では実現は難しいと思っております。

    よって、テストしたい場合は実際のAWS環境もしくはローカルにてEventBridge等の必要なサービスを稼働させ、Step Functions Localからコールするような対応が必要になるかと思います。

    ※Step Functions Localがコールするエンドポイント等は以下で設定可能です
    Step Functions コンフィギュレーションオプションのローカル設定 – AWS Step Functions

    ※3rd Party製の「LocalStack」というツールを使用すれば、必要なサービスをローカルで立ち上げテスト環境を構築することが可能です。
    GitHub – localstack/localstack: A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!

    さいごに

    Step Functions Localを使ったローカルテストを試してみました。
    AWS環境へサービスをデプロイする前に、ワークフローのみ事前に稼働確認できることは非常にメリットがあると感じました。事前確認することで、品質的に担保された状態で実際のサービスと連携できるようになりますし、うまく動作しなかった場合の切り分けも容易になると思います。

    興味ある方は是非お試しいただければと思います。

    以上