Local testing of CDK-defined Step Functions state machine

CDK-defined state machine

CDK ?

CDK stands for Cloud Development Kit, an open-source framework that allows to define your infrastructure as real code (Typescript, Python, Java, …) not just yaml or json templates. With the CDK CLI, this code is translated in CloudFormation templates, used to provision the resources on AWS.

State machine definition

In our case, we want to define a Step Functions state machine. AWS provides a Construct Library, where you can search for the resources you need. We will use the Step Functions module. The following code (in Typescript) shows how to define the different states of the machine and the state machine itself (full code is available here):

CDK2ASL

Later, we will need the state machine definition in the Amazon State Language format (“ASL”, which is JSON). To get this, we have 2 solutions (tldr; the 2nd is better):

1. Using outputs

This is the solution I initially used. Starting from the StateMachine construct, it is not possible to get the definition in this format. To get it we need to use a lower level construct, the CfnStateMachine which is the equivalent of the CloudFormation resource (note the Cfn in the beginning). Hopefully, we can retrieve the lower level construct from the higher level. Then, from this construct, we can retrieve the definition string and put it in output of the CloudFormation stack (just like any CloudFormation output):

{
“DirectBankAccountCreation”: {
workflowStateMachineDefinitionE0E0A7BF”: “{\”StartAt\”:\”Input checks\”,\”States\”:{\”Input checks\”:{\”Type\”:\”Parallel\”,\”Next\”:\”Create User\”,\”Branches\”:[{\”StartAt\”:\”Extract info from ID\”,\”States\”:{\”Extract info from ID\”:{\”Next\”:\”Crosscheck Identity\”,\”Retry\”:...\”User created, account creation initiated\”:{\”Type\”:\”Succeed\”}}}”, “userCreationAPIBankAccountCreationApiEndpoint1A0514D2”: “https://9xko5dgqbf.execute-api.eu-west-1.amazonaws.com/prod/"
}
}
jq -r ‘.DirectBankAccountCreation|with_entries(select(.key | startswith(“workflowStateMachineDefinition”))) | to_entries | .[].value’ cdk.out/output.json > cdk.out/state_machine.asl.json

2. Using a 3rd party tool

While writing this blog post, I discovered cdk-asl-extractor which simplifies this step a lot:

  • Second, use the CDK CLI and the cdk synth command to synthesise the CloudFormation template. It will generate the template in JSON format in the cdk.out folder (named [stackname].template.json).
  • Finally, run the tool with this template as parameter. In my case:
  • Second, we don’t have a useless output (which can be quite big).
  • And finally, you don’t need to fight with jq for hours to find how to extract a key with a random string 🌀🌩 ☠️❗️💥🤯…

Step Functions local

I think it’s pretty unique for AWS, Step Functions local is a downloadable version of Step Functions that lets you test your state machine locally. Once started, you can create a state machine, execute it, … just like you would do on the cloud but on your computer.

Mocked Service Integrations

In January 2022, AWS announced the Mocked Service Integrations for Step Functions Local. It allows to provide mock responses for service integrations and avoid reaching out to the cloud when testing a state machine locally.

Unit Testing dependencies not being mocker
Unit tests should be independent from external factors
  • Each tests case list the mocked states and the mocked response names.
  • Below, in the MockedResponses section, we have the definitions of each mock: either a Return with the expected payload returned by the mocked service, or a Throw to simulate an error.
  • You can get more details on this file in the doc.
  • The name of the test case: HappyPath (line 5)

Starting Step Functions local 🚀

Step Functions local exists in 2 versions: as an executable jar file and as a docker image. I choose to use the docker image and run it with the following command:

docker run -p 8083:8083 -d --rm --name stepfunctions-local \
--mount type=bind,readonly,source=$(ROOT_DIR)/test/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \
-e SFN_MOCK_CONFIG=”/home/StepFunctionsLocal/MockConfigFile.json” \ amazon/aws-stepfunctions-local

Creating the state machine

Once Step Functions local is running, we can create our state machine locally. We use the AWS CLI, specify the local endpoint, and use the state machine ASL definition previously retrieved. Please also note the name of the state machine, remember? It was the name of the state machine in the MockConfigFile.json so that Step Functions Local makes the link with the mocks:

aws stepfunctions create-state-machine \
--endpoint-url http://localhost:8083 \
# we can also use the asl-0.json file here
--definition file://cdk.out/state_machine.asl.json \
--name “DirectIntegrationTest” \
--role-arn “arn:aws:iam::123456789012:role/DummyRole” \
--no-cli-pager

Tests

This was the initial topic of this post: testing! And here we are. We now have Step Functions local running, and able to mock service integrations. We have a state machine available locally that we can execute. All that remains is to write the unit tests.

Unit tests, Unit tests everywhere

Setup

To test our state machine, we use the AWS SDK. As shown in the snippet below, we use the SFNClient with a local endpoint and the state machine that we’ve just created locally (DirectIntegrationTest).

Unit tests

Finally we can add our unit tests. As previously mentioned, we pass the test case name (here “HappyPath”) to get the appropriate mocks, and we simply execute the state machine with a JSON event. We then wait for the end of the execution and assert on the result:

Conclusion

The puzzle is complete, we’ve shown how to locally test your state machine, defined with CDK code. I’m a big fan of Step Functions! With its integration with almost all AWS APIs, I’m pretty sure it will become an important piece of serverless architectures, even replacing Lambda functions in many cases. State machines must be tested in the same way we test Lambda functions. They provide “business” logic and deserve the same attention. So, test your state machine!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jérôme Van Der Linden

Jérôme Van Der Linden

103 Followers

Senior Solution Architect @AWS - software craftsman, agile and devops enthusiastic, cloud advocate. Opinions are my own.