Take StepFunctions' TestState API one step further with samp-cli
Tags: aws, stepfunctions, opensource, tooling
Published: 2023-12-06 17:39:11

A lot of great features were released by the StepFunctions team just before and during re:invent 2023. One of them was the [TestState API](https://docs.aws.amazon.com/step-functions/latest/dg/test-state-isolation.html) that lets developers test individual states in isolation from the rest of the state machine. Until now, we've had to run the state machine from the beginning to reach the state we're working on.

AWS offers two ways to integrate with the API;
* Programmatically using the AWS-SDK - useful for unit/integration testing
* Via Workflow Studio in the AWS console - useful for iterating over changes in Workflow Studio:
![TestState UI 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzo5uoj43er067v8bkak.png)
![TestState UI 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hzezf927obifw3v7dcse.png)

Both are simple operations that let you target an ASL snippet (the task you want to test) with an optional input along with an IAM role, which is typically the execution role of the state machine.

Whilst this is a great feature that allows us to add a whole new level of testability to our state machines, I think there's a gap in developer experience that can easily be filled.

## The DX gap 
Developers spend their time in code editors and terminals of their choice. Every time they have to open up a web browser, let it be to comment on a Jira issue, Google/ChatGPT something or navigate the AWS console, they get subject to a cognitive distraction. To me, DX is about removing this distraction.

The TestState API is great for iterating over changes in an individual step and lets you massage the input/output processing or conditional logic in a choice state.

Unless you're testing the very first state in the state machine, you'll need to get a sensible input payload. Without having run through the previous steps, this can be difficult and prone to errors through typos or guesswork.

You're likely to look up an execution that successfully reached the step you're working on, copy that input and use it in your test. That's a lot of clicks.

## Use `samp-cli` to bridge the gap
[https://github.com/ljacobsson/samp-cli#readme](samp-cli) is a CLI tool aimed at bridging DX gaps for developers using AWS SAM.

Version 1.0.66 introduces support for the TestState API.

### Installation
`$ npm i -g samp-cli`

### Usage
There are three StepFunction's specific commands;
* `samp sfn init` - inits a new state machine in a SAM project
* `samp sfn sync` - establishes a live upstream sync between your local ASL and the cloud
* `samp sfn test-state` - lets you integrate with the TestState API

For the following example I'll use the `sync` and `test-state` sub commands.

Consider the following example state machine:
![Example state machine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bftzz7bcbr1nqh9sj7oq.png)

It takes an input of a payment id `{ "id": "..." }`, fetches the payment from DynamoDB, checks the payment status in a `Choice` state which sends the execution down the appropriate flow. 

However, upon running the state machine, each execution ends up in the `Unknown error` state:
![Failed execution](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g62e7nb6w7w8lld2pc0b.png)

We can assume that something is wrong with the `Choice` logic, so let's test that one in isolation. As we have an execution which we expected to succeed, we'll want to grab the input from that.

To achieve this I run `samp sfn test-state` in the same folder as my SAM template and `samconfig.(toml|yaml)`. The tool will attempt to parse your AWS profile, region and stack name from your samconfig. You can override these (see `samp sfn --help`).

In a different terminal I run `samp sfn sync` in order to get my changes to the ASL instantly reflected in my test account.

The following video demonstrates the steps I took to test, identify, fix and verify the bug I had in the Choice state - all in less than a minute and without leaving VSCode.

{% youtube e1SqKhJUxH4 %}

Note that reusing input from recent executions only works with standard workflows. For express you will need to use inputs from a JSON file or from manual input.