Amazon Web Services 한국 블로그

AWS Step Functions 신규 로컬 테스트 기능 향상을 통한 워크플로 개발 가속화

오늘 AWS Step Functions의 테스트 API인 TestState API를 통해 향상된 로컬 테스트 기능을 발표하게 되어 매우 기쁩니다.

이번에 확장된 기능은 API를 통해 제공되므로, 개발자가 선호하는 테스트 프레임워크를 사용해 워크플로 정의를 로컬 개발 환경에서 자동화된 테스트 스위트로 검증할 수 있습니다. 이를 통해 오류 처리 패턴, 데이터 변환, 서비스 통합 모킹 등을 로컬에서 손쉽게 테스트할 수 있습니다. 이번 출시에서는 API 기반의 로컬 유닛 테스트 방식을 도입하여 Amazon Web Services(AWS)에 배포하지 않고도 포괄적인 테스트 기능에 프로그래밍 방식으로 접근할 수 있게 되었습니다.

이번에 향상된 TestState API에서 도입된 핵심 기능은 다음 세 가지입니다.

  • 모킹 지원 – 다운스트림 서비스를 호출하지 않고도 상태 출력과 오류를 모킹할 수 있으므로, 상태 머신 로직의 실제 유닛 테스트가 가능합니다. TestState는 AWS API 모델에서 STRICT(기본값이며 모든 필수 필드를 검증), PRESENT(필드 유형과 이름만 검증), NONE(검증 없음)의 세 가지 검증 모드를 사용하여 모킹된 응답의 유효성을 검증합니다. 이를 통해 높은 신뢰도의 테스트를 수행할 수 있습니다.
  • 모든 상태 유형 지원 – Map 상태(인라인 및 분산형), Parallel 상태, 활동 기반 Task 상태, .sync 서비스 통합 패턴, .waitForTaskToken 서비스 통합 패턴 등의 고급 상태를 포함한 모든 상태 유형을 이제 테스트할 수 있습니다. 즉, 전체 워크플로 정의에서 TestState API를 사용할 수 있으며, 상태 전환, 오류 처리, 데이터 변환을 포함한 제어 흐름 로직을 검증하는 유닛 테스트를 작성할 수 있습니다.
  • 개별 상태 테스트 – 새로운 stateName 파라미터를 사용해 전체 상태 머신 정의 내 특정 상태를 테스트할 수 있습니다. 전체 상태 머신 정의를 한 번 제공해 두면, 이름으로 각 상태를 개별적으로 테스트할 수 있습니다. 이를 통해 특정 재시도 횟수, Map 반복 위치, 오류 시나리오 등 실행 컨텍스트를 제어하여 테스트할 수 있습니다.

향상된 TestState 시작하기
이제 향상된 TestState가 제공하는 새로운 기능을 살펴보겠습니다.

시나리오 1: 성공 결과 모킹

첫 번째 기능은 모킹 지원으로, 실제 AWS 서비스나 외부 HTTP 요청을 호출하지 않고도 워크플로 로직을 테스트할 수 있습니다. 빠른 유닛 테스트를 위해 서비스 응답을 모킹할 수도 있고, 통합 테스트를 위해 실제 AWS 서비스를 호출할 수도 있습니다. 모킹된 응답을 사용하는 경우 AWS Identity and Access Management(IAM) 권한이 필요하지 않습니다.

다음은 AWS Lambda 함수의 성공적인 응답을 모킹하는 방법입니다.

aws stepfunctions test-state --region us-east-1 \
--definition '{
  "Type": "Task",
  "Resource": "arn:aws:states:::lambda:invoke",
  "Parameters": {"FunctionName": "process-order"},
  "End": true
}' \
--mock '{"result":"{\"orderId\":\"12345\",\"status\":\"processed\"}"}' \
--inspection-level DEBUG

이 명령은 실제 Lambda 함수를 호출하지 않고 Lambda 호출 상태를 테스트합니다. TestState는 모킹된 응답을 Lambda 서비스 API 모델과 비교하여 실제 서비스가 반환하는 데이터 형식과 일치하는지 검증합니다.

응답은 성공적으로 실행된 결과와 함께 (DEBUG 검사 수준일 때의) 상세한 검사 데이터를 보여줍니다.

{
    "output": "{\"orderId\":\"12345\",\"status\":\"processed\"}",
    "inspectionData": {
        "input": "{}",
        "afterInputPath": "{}",
        "afterParameters": "{\"FunctionName\":\"process-order\"}",
        "result": "{\"orderId\":\"12345\",\"status\":\"processed\"}",
        "afterResultSelector": "{\"orderId\":\"12345\",\"status\":\"processed\"}",
        "afterResultPath": "{\"orderId\":\"12345\",\"status\":\"processed\"}"
    },
    "status": "SUCCEEDED"
}

모킹된 응답을 지정하면, TestState는 이를 해당 AWS 서비스의 API 모델과 비교해 모킹 데이터가 예상 스키마를 준수하는지 검증합니다. 이를 통해 실제 AWS 서비스를 호출하지 않고도 높은 정확도의 테스트를 유지할 수 있습니다.

시나리오 2: 오류 조건 모킹
오류 처리 로직을 테스트하기 위해 오류 조건도 모킹할 수 있습니다.

aws stepfunctions test-state --region us-east-1 \
--definition '{
  "Type": "Task",
  "Resource": "arn:aws:states:::lambda:invoke",
  "Parameters": {"FunctionName": "process-order"},
  "End": true
}' \
--mock '{"errorOutput":{"error":"Lambda.ServiceException","cause":"Function failed"}}' \
--inspection-level DEBUG

이 명령은 Lambda 서비스 예외를 시뮬레이션하므로, 실제 AWS 환경에서 오류를 발생시키지 않고도 상태 머신이 실패 상황을 어떻게 처리하는지 확인할 수 있습니다.

응답은 실행 실패 결과와 함께 오류 세부 정보가 보여줍니다.

{
    "error": "Lambda.ServiceException",
    "cause": "Function failed",
    "inspectionData": {
        "input": "{}",
        "afterInputPath": "{}",
        "afterParameters": "{\"FunctionName\":\"process-order\"}"
    },
    "status": "FAILED"
}

시나리오 3: Map 상태 테스트
두 번째 기능은 기존에 지원하지 않던 상태 유형을 새롭게 지원하는 것입니다. 다음은 Distributed Map 상태를 테스트하는 예시입니다.

aws stepfunctions test-state --region us-east-1 \
--definition '{
  "Type": "Map",
  "ItemProcessor": {
    "ProcessorConfig": {"Mode": "DISTRIBUTED", "ExecutionType": "STANDARD"},
    "StartAt": "ProcessItem",
    "States": {
      "ProcessItem": {
        "Type": "Task", 
        "Resource": "arn:aws:states:::lambda:invoke",
        "Parameters": {"FunctionName": "process-item"},
        "End": true
      }
    }
  },
  "End": true
}' \
--input '[{"itemId":1},{"itemId":2}]' \
--mock '{"result":"[{\"itemId\":1,\"status\":\"processed\"},{\"itemId\":2,\"status\":\"processed\"}]"}' \
--inspection-level DEBUG

모킹된 결과는 여러 항목을 처리한 전체 출력을 나타냅니다. 이 경우 모킹된 배열은 Map 상태의 예상 출력 형식과 일치해야 합니다.

응답은 배열 입력이 성공적으로 처리된 결과를 보여줍니다.

{
    "output": "[{\"itemId\":1,\"status\":\"processed\"},{\"itemId\":2,\"status\":\"processed\"}]",
    "inspectionData": {
        "input": "[{\"itemId\":1},{\"itemId\":2}]",
        "afterInputPath": "[{\"itemId\":1},{\"itemId\":2}]",
        "afterResultSelector": "[{\"itemId\":1,\"status\":\"processed\"},{\"itemId\":2,\"status\":\"processed\"}]",
        "afterResultPath": "[{\"itemId\":1,\"status\":\"processed\"},{\"itemId\":2,\"status\":\"processed\"}]"
    },
    "status": "SUCCEEDED"
}

시나리오 4: Parallel 상태 테스트
마찬가지로, 여러 브랜치를 동시에 실행하는 Parallel 상태도 테스트할 수 있습니다.

aws stepfunctions test-state --region us-east-1 \
--definition '{
  "Type": "Parallel",
  "Branches": [
    {"StartAt": "Branch1", "States": {"Branch1": {"Type": "Pass", "End": true}}},
    {"StartAt": "Branch2", "States": {"Branch2": {"Type": "Pass", "End": true}}}
  ],
  "End": true
}' \
--mock '{"result":"[{\"branch1\":\"data1\"},{\"branch2\":\"data2\"}]"}' \
--inspection-level DEBUG

모킹된 결과는 각 브랜치당 하나의 요소가 있는 배열이어야 합니다. TestState를 사용하면 실제 Parallel 상태 실행에서 생성될 데이터 구조와 동일한 형태로 모킹 데이터를 구성할 수 있습니다.

응답은 병렬 실행 결과를 보여줍니다.

{
    "output": "[{\"branch1\":\"data1\"},{\"branch2\":\"data2\"}]",
    "inspectionData": {
        "input": "{}",
        "afterResultSelector": "[{\"branch1\":\"data1\"},{\"branch2\":\"data2\"}]",
        "afterResultPath": "[{\"branch1\":\"data1\"},{\"branch2\":\"data2\"}]"
    },
    "status": "SUCCEEDED"
}

시나리오 5: 전체 워크플로 내 개별 상태 테스트
stateName 파라미터를 사용해 전체 상태 머신 정의 내 특정 상태를 테스트할 수 있습니다. 아래 예시는 하나의 상태만 보여주지만, 보통 전체 워크플로 정의를 제공한 뒤 테스트할 상태를 지정합니다.

aws stepfunctions test-state --region us-east-1 \
--definition '{
  "Type": "Task",
  "Resource": "arn:aws:states:::lambda:invoke",
  "Parameters": {"FunctionName": "validate-order"},
  "End": true
}' \
--input '{"orderId":"12345","amount":99.99}' \
--mock '{"result":"{\"orderId\":\"12345\",\"validated\":true}"}' \
--inspection-level DEBUG

이 예시는 특정 입력 데이터를 기반으로 Lambda 호출 상태를 테스트하며, TestState가 입력을 어떻게 처리하고 상태 실행을 통해 어떻게 변환하는지를 보여줍니다.

응답은 입력 처리와 검증 과정을 상세하게 보여줍니다.

{
    "output": "{\"orderId\":\"12345\",\"validated\":true}",
    "inspectionData": {
        "input": "{\"orderId\":\"12345\",\"amount\":99.99}",
        "afterInputPath": "{\"orderId\":\"12345\",\"amount\":99.99}",
        "afterParameters": "{\"FunctionName\":\"validate-order\"}",
        "result": "{\"orderId\":\"12345\",\"validated\":true}",
        "afterResultSelector": "{\"orderId\":\"12345\",\"validated\":true}",
        "afterResultPath": "{\"orderId\":\"12345\",\"validated\":true}"
    },
    "status": "SUCCEEDED"
}

이 향상된 기능들은 익숙한 로컬 개발 환경을 Step Functions 워크플로에 그대로 가져와, AWS 계정에 배포하기 전에 변경 사항에 대한 즉각적인 피드백을 받을 수 있도록 도와줍니다. 이를 통해 모든 Step Functions 특성을 클라우드 실행과 동일한 신뢰도로 검증하는 자동화된 테스트 스위트를 작성할 수 있으며, 배포 시 워크플로가 예상대로 작동한다는 확신을 줍니다.

알아야 할 사항
알아두어야 할 요점은 다음과 같습니다.

  • 가용성 – 향상된 TestState 기능은 Step Functions가 지원되는 모든 AWS 리전에서 사용할 수 있습니다.
  • 요금 – TestState API 호출은 추가 요금 없이 AWS Step Functions 요금에 포함됩니다.
  • 프레임워크 호환성 – TestState는 HTTP 요청을 보낼 수 있는 모든 테스트 프레임워크(Jest, pytest, JUnit 등)와 호환됩니다. 배포 전에 지속적 통합 및 지속적 전달(CI/CD) 파이프라인에서 워크플로를 자동으로 검증하는 테스트 스위트를 작성할 수 있습니다.
  • 특성 지원 – 향상된 TestState는 Distributed Map, Parallel 상태, 오류 처리, JSONata 표현식을 포함한 모든 Step Functions 특성을 지원합니다.
  • 설명서 – 다양한 구성 옵션에 대한 자세한 내용은 TestState 설명서 및 업데이트된 요청/응답 모델의 API 레퍼런스를 참조하세요.

지금 바로 TestState를 개발 워크플로에 통합하여 향상된 로컬 테스트를 시작해 보세요.

즐겁게 빌드해 보세요!
Donnie