AWS Step Functionsステートマシンの結合テストをしたい(実行成功確認)

2021.12.17

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

今回は、AWS Step Functionsステートマシンの結合テストをする方法を確認してみました。

ステートマシンの実行結果の評価がうまく行えない

AWS上に作成をしたStep Functionsステートマシンに対する動作確認(いわゆる結合テスト)を行いたい場合があります。

テスト対象は、このようなLambda関数を1つ実行するタスクがある5秒程度で終了するステートマシンとします。

そこで次のような、ステートマシンをAWS SDKで実行して実行結果が成功(SUCCEEDED)となることを確認するテストコードを作成しました。

上手くいかないテストコード

import * as AWS from 'aws-sdk';

const sfn = new AWS.StepFunctions({
  region: 'ap-northeast-1',
});

const STATE_MACHINE_ARN = process.env.STATE_MACHINE_ARN as string;

test('ステートマシンの実行が成功することを確認', async (): Promise<void> => {
  //テスト対象のステートマシンを実行
  const execution = await sfn
    .startExecution({
      stateMachineArn: STATE_MACHINE_ARN,
    })
    .promise();

  const executionArn = execution.executionArn;

  //ステートマシンの実行情報を取得
  const executionDescription = await sfn
    .describeExecution({ executionArn: executionArn })
    .promise();

  //ステートマシンの実行が成功していることを確認
  expect(executionDescription.status).toBe('SUCCEEDED');
});

DescribeExecutionで確認できるステートマシンの実行ステータスの値は次のようなものがあります。このうちSUCCEEDEDとなれば成功、FAILEDTIMED_OUTとなれば失敗であることが分かります。

status
The current status of the execution.
Type: String
Valid Values: RUNNING | SUCCEEDED | FAILED | TIMED_OUT | ABORTED

Jestでテストを実行します。するとステートマシンの実行ステータスがRUNNING(実行中)の状態でexpect()による評価が行われてしまい、成功か失敗かの評価がうまく行えませんでした。

$ npx jest
 FAIL  test/stateMachine.test.ts (6.628 s)
  ✕ ステートマシンの実行が成功することを確認 (298 ms)

  ● ステートマシンの実行が成功することを確認

    expect(received).toBe(expected) // Object.is equality

    Expected: "SUCCEEDED"
    Received: "RUNNING"

      20 |     .promise();
      21 |
    > 22 |   expect(executionDescription.status).toBe('SUCCEEDED');
         |                                       ^
      23 | });
      24 |

      at Object.<anonymous> (test/stateMachine.test.ts:22:39)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        6.684 s, estimated 8 s
Ran all test suites.

これはstartExecutionによるステートマシンの実行は非同期となるためです。

待機時間を設けることによりうまく行えた

そこでwhilesetTimeoutによる待機時間を設けます。ステートマシンの実行ステータスがRUNNINGの間は待機をするようにします。

上手くいったテストコード

import * as AWS from 'aws-sdk';

const sfn = new AWS.StepFunctions({
  region: 'ap-northeast-1',
});

const STATE_MACHINE_ARN = process.env.STATE_MACHINE_ARN as string;

test('ステートマシンの実行が成功することを確認', async (): Promise<void> => {
  //テスト対象のステートマシンを実行
  const execution = await sfn
    .startExecution({
      stateMachineArn: STATE_MACHINE_ARN,
    })
    .promise();

  const executionArn = execution.executionArn;

  //ステートマシンの実行ステータスがRUNNINGでなくなるまで待機
  let executionStatus = 'RUNNING';
  while (executionStatus === 'RUNNING') {
    await new Promise((r) => setTimeout(r, 1000));

    let res = await sfn
      .describeExecution({ executionArn: executionArn })
      .promise();

    if (res.status !== 'RUNNING') executionStatus = res.status;
  }

  //ステートマシンの実行情報を取得
  const executionDescription = await sfn
    .describeExecution({ executionArn: executionArn })
    .promise();

  //ステートマシンの実行が成功していることを確認
  expect(executionDescription.status).toBe('SUCCEEDED');
});

するとJestによる実行結果の評価を行えるようになりました。

$ npx jest
 PASS  test/stateMachine.test.ts (8.251 s)
  ✓ ステートマシンの実行が成功することを確認 (1769 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.299 s
Ran all test suites.

startSyncExecutionは使えないのか?

今回、テストコードからステートマシンを実行するためにstartExecutionを使用しました。このメソッドは標準ワークフローのステートマシンの実行にのみ利用可能です。今回テストを行いたかったのは標準ワークフローとなります。

StartExecution is idempotent for STANDARD workflows. For a STANDARD workflow, if StartExecution is called with the same name and input as a running execution, the call will succeed and return the same response as the original request.

もうひとつステートマシンを実行できるメソッドとしてStartSyncExecutionというものがありますが、これは標準ワークフローのステートマシンには使用できません。Expressワークフローのものに対してのみ使用できます。

Starts a Synchronous Express state machine execution. StartSyncExecution is not available for STANDARD workflows.

両者の違いとしては、標準ワークフローの実行は非同期のみとなりますが、Expressワークフローは同期での実行が可能となります。

当初、これを使用すると名前的にステートマシンの同期実行ができそうな気がして使ってみたのですが、標準ワークフローのステートマシンであったため、案の定エラーとなりました。

import * as AWS from 'aws-sdk';

const sfn = new AWS.StepFunctions({
  region: 'ap-northeast-1',
});

const STATE_MACHINE_ARN = process.env.STATE_MACHINE_ARN as string;

test('ステートマシンの実行', async (): Promise<void> => {
  const execution = await sfn
    .startSyncExecution({
      stateMachineArn: STATE_MACHINE_ARN,
    })
    .promise();

  console.log(execution);
});

StateMachineTypeNotSupported: This operation is not supported by this type of state machineというエラーとなっています。

$ npx jest
 FAIL  test/stateMachine.test.ts (6.506 s)
  ✕ ステートマシンの実行 (179 ms)

  ● ステートマシンの実行

    StateMachineTypeNotSupported: This operation is not supported by this type of state machine

      at Request.extractError (node_modules/aws-sdk/lib/protocol/json.js:52:27)
      at Request.callListeners (node_modules/aws-sdk/lib/sequential_executor.js:106:20)
      at Request.emit (node_modules/aws-sdk/lib/sequential_executor.js:78:10)
      at Request.emit (node_modules/aws-sdk/lib/request.js:686:14)
      at Request.transition (node_modules/aws-sdk/lib/request.js:22:10)
      at AcceptorStateMachine.runTo (node_modules/aws-sdk/lib/state_machine.js:14:12)
      at node_modules/aws-sdk/lib/state_machine.js:26:10
      at Request.<anonymous> (node_modules/aws-sdk/lib/request.js:38:9)
      at Request.<anonymous> (node_modules/aws-sdk/lib/request.js:688:12)
      at Request.callListeners (node_modules/aws-sdk/lib/sequential_executor.js:116:18)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        6.555 s, estimated 11 s
Ran all test suites.

以上