Hello people. I have this Lambda function for the ...
# help
ö
Hello people. I have this Lambda function for the POST endpoint. I want to write the test for this function, but I’m really not sure how I will mock the
event
, and
EventBridge
.
Copy code
import { EventBridgeClient, PutEventsCommand, PutEventsCommandInput } from '@aws-sdk/client-eventbridge'
import { StatusCodes } from 'http-status-codes'

import type { APIGatewayProxyHandler } from "aws-lambda"

const eventBridgeClient = new EventBridgeClient({ region: 'eu-central-1' })

export const handler: APIGatewayProxyHandler = async (evt, ctx, cb) => {
      const requestBody = JSON.parse(evt.body)
      const { orderID, price } = requestBody
      //////////
      // Check if the body has required properties
      //////////
      if (!orderID || !price) {
            return {
                  statusCode: StatusCodes.BAD_REQUEST,
                  body: 'Invalid request'
            }
      }
      //////////
      // Put it into EventBridge
      //////////
      try {
            const putEventsCommandInput: PutEventsCommandInput = {
                  Entries: [
                        { EventBusName: 'OrderBus', Detail: JSON.stringify({ id: orderID, price }) }
                  ]
            }
            const putEventsCommand = new PutEventsCommand(putEventsCommandInput)
            const putEventsCommandOutput = await eventBridgeClient.send(putEventsCommand)
            return {
                  statusCode: StatusCodes.CREATED,
                  body: JSON.stringify({ eventID: putEventsCommandOutput.Entries[0].EventId })
            }
      } catch (error) {
            return {
                  statusCode: StatusCodes.ACCEPTED,
                  body: JSON.stringify(error)
            }
      }

}
t
I don't mock any services for tests
Test them against the real thing
ö
But how will I write the unit test then
r
For unit testing, I don't have an example for EventBridge but this example with a DynamoDB stream is the same principle.
Copy code
import { DynamoDBStreamEvent, Context, Callback, DynamoDBRecord } from 'aws-lambda';


test('it should handle the stream event successfully', async () => {
  /*******************
   * SETUP
   *******************/
  const event: DynamoDBStreamEvent = {
    Records: [
      {
        //...
      }
    ],
  };

  /*******************
   * ACT
   *******************/
  // Call the handler
  await handler(event, {} as Context, {} as Callback);

  /*******************
   * VERIFY
   *******************/
  // Expect the build responses service to be called once with an array of the four new activities
  expect(someMockedService.buildResponses).toBeCalledTimes(1);
  ...
  
});
t
My unit tests typically only mocks the input event and sends it to aws
r
I'd argue that was more of a partial integration test 😉
s
Can you expand on that @thdxr? I understand what you mean by mocking the input (like @Ross Coundons example), but I'm not sure what you mean by "sends it to AWS"
Right, it seems like the distinction that's being made here is unit vs integration testing. I'm not clarifying this point to split hairs, I'm just trying to understand the approach
t
I guess what I'm saying is I don't really do unit tests and all my tests are effectively integration tests
s
Gotcha
That makes sense
For example, you'd send a pre-defined event to your lambda function, but all the other interactions (in the above example, Event Bridge) interact with the "real" services.
t
Yeah exactly. My views on testing are a bit extreme so take with a grain of salt but I generally don't believe unit tests are useful. Think it's hard to write a truly useful unit test that actually saves you when refactoring. I tend to rely on integration tests where I make sure inputs into the system have the expected outputs - everything in the middle is a blackbox. If that blackbox is refactored as long as the same inputs result in the same outputs I feel confident. Of course there might be some tricky business logic in there I might want to extract out and unit test (eg calculating some complex tax code) but 99% of my tests are integration
The reason I called what I was doing "unit tests" is because unfortunately I'm using a unit testing framework at the moment to do these integration tests. It's fine but I wonder if a more focused tool can be built - esp for serverless
s
There's this one https://github.com/Theodo-UK/sls-test-tools. personally I like the idea of a framework that would use step functions as a testing harness. That way they can invoke your lambdas in the real environment, passing in factory generated aws events. We can then pass the output to the assert lambda function, or assert side effects. Also we can run a huge test suite massively in parallel
s
I have been thinking this for a while. Mostly for scale reasons. I like the idea of taking a large jest suite and running it in parallel in lambda in like 10 seconds aswell. I have seen projects use step functions for test suites before, it gives a good level of flow control
In general though on the above, I would only write integration tests. If I need to I will replicate stateful components (databases) locally. Also, would use nock, with nock recorder to hit live resources, 3rd parties etc. with the ability to replay when testing small changes locally