|
| 1 | +--- |
| 2 | +published: false |
| 3 | +title: '' |
| 4 | +cover_image: |
| 5 | +description: '' |
| 6 | +tags: |
| 7 | +series: |
| 8 | +canonical_url: |
| 9 | +--- |
| 10 | + |
| 11 | +## TL;DR |
| 12 | + |
| 13 | +Learn how to perform integration tests iso prod with aws serverless services. Using lambda-http-interceptor you can easily intercept and mock http calls coming from your deployed lambda functions. |
| 14 | + |
| 15 | +**Why should you use:** |
| 16 | +- You want to test your lambda functions in a iso-prod environment |
| 17 | +- You want to save money while running integration tests by not triggering costly third party APIs |
| 18 | +- You want to control the behavior of third party APIs to test edge cases |
| 19 | +- You don't want to change your lambda code to make it testable |
| 20 | + |
| 21 | +And maybe you can use it for all theses reasons at the same time! |
| 22 | + |
| 23 | +{% cta https://github.com/sls-mentor/sls-mentor %} Try lambda-http-interceptor here 😉 {% endcta %} |
| 24 | + |
| 25 | +## Intercept http calls in lambda functions |
| 26 | + |
| 27 | +The lib is made of a CDK Construct to instantiate in your stack. |
| 28 | + |
| 29 | +The `HttpInterceptor` **construct needs to be instantiated in the stack**, or inside another Construct. And then **the interceptor needs to be applied to the lambda function** http calls need to be intercepted from. |
| 30 | + |
| 31 | +The `applyHttpInterceptor` uses `Aspects` in order to apply it on each `NodeLambdaFunction` it finds, thus `applyHttpInterceptor` takes any Construct as input. |
| 32 | + |
| 33 | +```ts |
| 34 | +import { Stack, StackProps } from "aws-cdk-lib"; |
| 35 | + |
| 36 | +import { Construct } from "constructs"; |
| 37 | +import { HttpInterceptor, applyHttpInterceptor } from "lambda-http-interceptor"; |
| 38 | +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; |
| 39 | +import { Runtime } from "aws-cdk-lib/aws-lambda"; |
| 40 | + |
| 41 | +export class MyStack extends Stack { |
| 42 | + constructor(scope: Construct, id: string, props?: StackProps) { |
| 43 | + super(scope, id, props); |
| 44 | + |
| 45 | + const interceptor = new HttpInterceptor(this, "HttpInterceptor"); |
| 46 | + |
| 47 | + const myLambdaFunctionThatMakesExternalCalls = new NodejsFunction( |
| 48 | + this, |
| 49 | + "MakeExternalCalls", |
| 50 | + { |
| 51 | + runtime: Runtime.NODEJS_18_X, |
| 52 | + handler: "index.handler", |
| 53 | + entry: './handler.ts', |
| 54 | + }, |
| 55 | + ); |
| 56 | + |
| 57 | + applyHttpInterceptor(myLambdaFunctionThatDoesExternalCalls, interceptor); |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +After deploying, everything is setup on the stack to then perform integration tests. |
| 63 | + |
| 64 | +The second part of the lib is a set of **tools to perform integration tests**. They are gathered in the `HttpLambdaInterceptorClient` class. |
| 65 | + |
| 66 | +```typescript |
| 67 | +import fetch from "node-fetch"; |
| 68 | +import { expect, describe, it } from "vitest"; |
| 69 | + |
| 70 | +process.env.HTTP_INTERCEPTOR_TABLE_NAME = '<table-name-from-construct>' |
| 71 | + |
| 72 | +import { HttpLambdaInterceptorClient } from "lambda-http-interceptor"; |
| 73 | + |
| 74 | +import { triggerMyLambdaFunctionThatMakesExternalCalls } from './utils'; |
| 75 | + |
| 76 | +describe("my test", () => { |
| 77 | + const interceptorClient = new HttpLambdaInterceptorClient( |
| 78 | + '<myLambdaFunctionThatMakesExternalCalls-name>', |
| 79 | + ); |
| 80 | + it("tests my lambda function", async () => { |
| 81 | + await interceptorClient.createConfigs([ |
| 82 | + { |
| 83 | + url: "https://api-1/*", |
| 84 | + response: { |
| 85 | + status: 404, |
| 86 | + body: JSON.stringify({ |
| 87 | + errorMessage: "Not found", |
| 88 | + }), |
| 89 | + }, |
| 90 | + }, |
| 91 | + { |
| 92 | + url: "https://api-2/path", |
| 93 | + response: { |
| 94 | + passThrough: true, |
| 95 | + }, |
| 96 | + }, |
| 97 | + ]); |
| 98 | + await triggerMyLambdaFunctionThatMakesExternalCalls(); |
| 99 | + const interceptedCalls = await interceptorClient.pollInterceptedCalls({ |
| 100 | + numberOfCallsToExpect: 2, |
| 101 | + timeout: 5000, |
| 102 | + }); |
| 103 | + expect(resp.interceptedCalls).toBe(2); |
| 104 | + }); |
| 105 | +}); |
| 106 | +``` |
| 107 | + |
| 108 | +## How does it work? |
| 109 | + |
| 110 | +### lambda-http-interceptor stores the configurations and the call made in DynamoDB table |
| 111 | + |
| 112 | +The `HttpInterceptor` instantiates a DynamoDB table in the stack. The table is used to store the configurations of the http calls to intercept. When performing integration tests, filling up the table with configuration is done using the `createConfigs` method of the `HttpLambdaInterceptorClient` class. |
| 113 | + |
| 114 | +Then assertions can be made on the calls made by the lambda after they are fetched using the `pollInterceptedCalls` method of the `HttpLambdaInterceptorClient` class. |
| 115 | + |
| 116 | +> Don't forget to give the user you're using to perform the integration tests the right to read in the table. In general, we use AdministratorAccess role for the user performing these tasks. |
| 117 | +
|
| 118 | +### lambda-http-interceptor uses an internal extension to intercept http calls in lambda functions |
| 119 | + |
| 120 | +The internal extension that the interceptor deploys on the lambda functions overrides the `http` module of nodejs that is used to make http calls. |
| 121 | + |
| 122 | +For each call made by the lambda, it fetches the http calls configuration stored in DynamoDB and either passes through the call or returns the response value configured at the start. |
| 123 | + |
| 124 | +It keeps track of the http calls listed that are listed in the configuration. If the response of a call doesn't need to be changed but it still needs to be tracked in order to make assertions on it, the configuration of the call doesn't change and the response only contains `passthrough: true`. |
| 125 | + |
| 126 | +If you want to deep dive the functioning of the interceptor, you can check out [this article](https://dev.to/slsbytheodo/power-up-your-serverless-application-with-aws-lambda-extensions-3a31) that presents extensions really clearly using a simple example. |
| 127 | + |
| 128 | +## lambda-http-interceptor has everything built in |
| 129 | + |
| 130 | +The setup is fairly easy and it can be used to make assertions on the calls made by your deployed lambda functions. The documentation is far more exhaustive to get you started. |
| 131 | + |
| 132 | +{% cta https://github.com/sls-mentor/sls-mentor %} Try lambda-http-interceptor here 😉 {% endcta %} |
| 133 | + |
| 134 | +Don't hesitate to star it ⭐️ |
0 commit comments