Is there a way to run integration tests interactiv...
# sst
r
Is there a way to run integration tests interactively during the development process. As an example, previously, when using serverless framework, I'd typically have dynamodb local running and run some tests directly using the jest-runner vscode plugin by customising the endpoint. I'd like to do the same but with a real dynamodb table. I'm about to start tinkering with this but wondered what others have found
I'm actually pretty confused around the use of sst test for integration testing as it seems I can't specify a stage and I can't see much in the way of documentation for how this should be used
s
I don't have a suggestion, but am following with interest. I am about the embark on the same process and want to hear what others are doing
t
sst test doesn't really do anything except call jest for you with some default options so you don't have to specify a config
r
I think I'm missing something 🙂 or more likely, a few things. I'll try to list them out as discreet questions: 1. We have a tonne of existing tests and we configure jest using a jest.config.js which includes things like moduleNameMapper to help deal with aliases we have set up in the code. If I run
sst test --config jest.config.js
I get the error
Copy code
argv.config.match is not a function
2. I'd like to run our Jest integration tests using real sst resources - effectively initiate the tests while
sst start
is running. I can't picture how this can be done right now
Any thoughts?
t
I'd recommend not using
sst test
- it's a layer of abstraction that's useful for people for basic projects but is unnecessary if you're doing more advanced things. In terms of running tests against a running
sst start
- there's not really anything sst specific here. You'd write tests that hit certain API endpoints and set that with a variable you pass in
I do this for cypress tests, my cypress suite runs normally and hits what seems like a normal API endpoint - it's unaware it's actually hitting an sst start instance
r
I can't quite picture the plumbing here. We run Jest which starts the tests, these tests check various things against AWS and 3rd party services. Say I run it on my own machine, how am I telling Jest to connect to a service that's part of the SST runtime?
t
Can you give me an example of something you want to test?
r
Ok, so let's say an eventbridge cron kicks off a lambda that retrieves some data, manipulates it and writes to DDB
t
It's kind of hard tot est cron in any system
r
Yeah, so we take that out of the equation and pass an eventbridge event to a handler
using jest
in particular, how do we do the dynamodb write?
s
Maybe it would be instructive to use a (completely made up) code snippet? Wouldn't it look similar to testing an API endpoint? Nothing really SST specific here
Copy code
const myApiEndpoint = "<https://bgrsbgy.execute-api.us-east-1.amazonaws.com>"
const request = require('supertest')(myApiEndpoint)

describe('/foobar'), () => {
  it('should do a thing',() => {
     const response = <http://request.post|request.post>('/foobar')
     assert(response.body).toHaveProperty('theThing','...')
  }
}
or did you have something else in mind?
t
So what I typically do here is think in terms of inputs + outputs. This is an end to end test and your input is the event bridge event and the output is the dynamodb write. To be honest, dealing with this is kind of tricky right now in serverless and we need better tools. This is also one of the reasons I store everything in SSM params. So for me this looks like 1. Run
sst start
2. Environment is bootstrapped and values like the bus name are stored in SSM with something like
/stagename/BUS_NAME
3. Run
jest
with an env variable like
SSM_PREFIX=/stagename/
4. The test will read the bus from ssm and then send an event to it. 5. I guess then it can sleep for some time then look for the dynamodb write - again by using SSM to lookup the table name
I really need to get my open source example project completed because it demonstrates how to use this pattern practically. I have a bit of setup to make some of the rough edges easier to work with
Ultimately there's nothing SST specific here, the key thing is to store all variables in SSM so that running your code from anywhere, whether it's jest or a script, can talk to the right things
Basically what @Seth Geoghegan posted except instead of hardcoding that url, you'd load it from SSM
r
...processing...
t
haha yeah it's tough to get what I'm saying from an explanation, which is why I think an example is needed
r
All we really store in SSM at the mo are credentials, everything else is env vars. So all the things like table names, queue names etc are pushed into the function environments in the stack creation. If we're running a jest session separately, the tricky thing is going to be getting those env vars where we need them
t
So my little app it just tracks links that are shared in slack so you can get a feed of what resources people are sharing. Here is the
Links.found()
function that gets triggered once I see a link in a slack message
Copy code
export async function found(detail: FoundEvent["detail"]) {
  const evt: FoundEvent = {
    source: "tempest",
    type: "link.found",
    detail,
  }
  await Bus.publish(evt)
}
This is calling
Bus.publish(evt)
which is a wrapper around EventBridge to publish an event to my app's event bus:
Copy code
export function publish(...events: Event[]) {
  return bus
    .putEvents({
      Entries: events.map((item) => ({
        EventBusName: Config.BUS_NAME,
        Detail: JSON.stringify(item.detail),
        Source: item.source,
        DetailType: item.type,
      })),
    })
    .promise()
}
This is loading the Bus name from
Config.BUS_NAME
which is loaded on app boot from SSM. This is really ugly code because I use
deasync
can ignore the datails - it just does a scan on SSM for `/stage/*`: Then jest only needs one environment variable passed which is the stage name
Yeah that's exactly why I switched from env variables to SSM. You end up having to build up a ton of values to pass into other contexts you need to run your code - eg jest
r
funny, we were just discussing about how we had too many
maybe this is the kick up the arse we need to change
t
I wanted to make using SSM easier in SST, simple
this.addSSM
option. And then maybe even provide a simple library for loading them into memory in js
s
This is an interesting discussion. Searching far and wide for different strategies for testing in a serverless environment, nearly every article starts with how to mock calls to AWS
Which is precisely what I'm trying to avoid
There is so much mocking bloat in the tests I'm currently working with 🥴
t
Yeah I don't do any mocks
r
well we have a lot of unit tests where we do mock the various services, which for us, particularly in a TypeScript world, give us good and fast feedback on valid inputs and outputs. The next step is testing across the boundaries within an SST deployed app
s
The more I think about using SSM in this way, the more I like it. I love how the common theme is to "Mock All The Things", and you're over here using SSM in place of environment variables in your tests like a goddamn madman 😆
t
r
His view sucks. (Sorry, bad joke about the fact he works on roombas)
s
It's such a clean and accessible solution though
i get his point though (err...if his point is that an SSM value will change out from under the deployment could cause weird behaviors)
t
My issue which I can't really say publicly is yes he's technically right. Someone can go in and change the value and break things. But practically that won't happen + you get a bunch of benefits. Feel like a lot of these people are stuck in theory land which is why serverless isn't more adopted
r
Well, I think you can have it both ways, if you reference a specific version of the parameter via the setup env var, until you change the version in that env var and redeploy, you won't pick the new version
s
Also, this seems like a particularly useful pattern for testing
I think this is a really interesting pattern. At the very least, it gets people talking about why someone would do something like this. That kind of discussion pushes the community forward