Search code examples
javaaws-lambdaspocktestcontainerslocalstack

LocalStackContainer integration testing a lambda


Is there a way we can (integration) test a Lambda for AWS using testcontainers & the following LocalStackContainer:

new LocalStackContainer(DockerImageName.parse('localstack/localstack:0.11.6'))
    .withServices(LocalStackContainer.Service.LAMBDA)

Specifically, how do I "upload" the Lambda to the locally running service, so that I can then trigger & test it?


Solution

  • I've managed to setup Localstack and a MySql testcontainer (in groovy spock) like so:

      @Shared
      public static Network network = Network.newNetwork()
    
      @Shared
      @ClassRule
      public static MySQLContainer mySql = new MySQLContainer(DockerImageName.parse('mysql:5.7'))
        .withNetwork(network)
        .withNetworkAliases(MY_SQL_HOST_NAME) as MySQLContainer
    
      @Shared
      @ClassRule
      public static LocalStackContainer localStack = new LocalStackContainer(DockerImageName.parse('localstack/localstack:0.11.6'))
        .withServices(SQS, LAMBDA, CLOUDWATCH, DYNAMODB, KMS, STS, IAM)
        .withNetwork(network)
        .withEnv('LAMBDA_DOCKER_NETWORK', network.name)
        .withCopyFileToContainer(MountableFile.forHostPath(new File('the jar file').path), '/opt/code/localstack/lambda.jar')
        .withCopyFileToContainer(MountableFile.forClasspathResource('seed.yaml'), '/init/seed.yaml')
    
    • This sets up a bridged network so that the two containers can communicate with each other. Also setting LAMBDA_DOCKER_NETWORK allows the lambda to communicate with MySql, SQS etc.
    • The lambda jar file is copied to the container (withCopyFileToContainer)
    • I also needed KMS, so seed.yaml initialises it (see Local KMS documentation)

    Once the container is up and running the lambda can be deployed using awslocal and standard CLI commands:

        def result = localStack.execInContainer(
          'awslocal', 'lambda', 'create-function',
          '--function-name', 'lambda-name',
          '--runtime', 'java8',
          '--handler', 'uk.co.blah.blah.Handler',
          '--role', 'arn:aws:iam::123456:role/irrelevant',
          '--zip-file', 'fileb://lambda.jar',
          '--environment', "Variables={MY_SQL_USERNAME=${mySql.username},MY_SQL_PASSWORD=${encryptedDbPassword},PLATFORM_DB_URL=${"jdbc:mysql://${MY_SQL_HOST_NAME}:3306"}," +
            "AWS_REGION=${localStack.region}}"
        )
    

    Finally, the lambda can be manually triggered in tests like so:

    localStack.execInContainer('awslocal', 'lambda', 'invoke', '--function-name', 'lambda-name', 'output0.json')