AWS Lambda 环境变量和依赖注入 [英] AWS Lambda Environment Variables and Dependency Injection

查看:39
本文介绍了AWS Lambda 环境变量和依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在将 AWS Lambda 与 .NET Core v1.0 结合使用时,是否有任何最佳实践或文档可用于使用依赖注入或模拟环境变量?

Are there any best practices or documentation available for using Dependency Injection or mocking Environment Variables when using AWS Lambda with .NET Core v1.0 ?

作为示例,下面是一个示例 Lambda 函数 ProcessKinesisMessageById,它接受 KinesisEvent 并进行某种处理.此处理的一部分涉及访问某种需要访问环境变量以进行设置的外部服务(如 AWS S3 或数据库).

As an example, below is an example Lambda function ProcessKinesisMessageById that accepts a KinesisEvent and does some sort of processing. Part of this processing involves accessing some sort of External Service (like AWS S3 or a database) that need access to Environment Variables for setup.

public class AWSLambdaFileProcessingService
{
    private IFileUploadService _fileUploadService;

    // No constructor in the Lambda Function

    [LambdaSerializer(typeof(JsonSerializer))]
    public void ProcessKinesisMessageById(KinesisEvent kinesisEvent, ILambdaContext context)
    {
        Console.WriteLine("Processing Kinesis Request");

        _fileUploadService = new AWSFileUploadService(); // Can this be injected? (Constructor shown below)

        // some sort of processing
        _fileUploadService.DoSomethingWithKinesisEvent(kinesisEvent);
    }
}

// Example of of a class that needs access to environment variables
// Can this class be injected into the AWS Lambda function?  
// Or the Environment Variables mocked?
public class AWSFileUploadService : IFileUploadService
{
    private readonly IAmazonS3 _amazonS3Client;
    private readonly TransferUtility _fileTransferUtility;


    public AWSFileUploadService()
    {
        _amazonS3Client = new AmazonS3Client(
            System.Environment.GetEnvironmentVariable("AWS_S3_KEY"),
            System.Environment.GetEnvironmentVariable("AWS_S3_SECRET_KEY")
            );

        _fileTransferUtility = new TransferUtility(_amazonS3Client);
    }

    public bool DoSomethingWithKinesisEvent(KinesisEvent kinesisEvent)
    {
        // ....
    }

```

该函数在使用环境变量发布后工作正常,发布到 AWS 后可以使用 Lambda 函数视图测试控制台(在 Visual Studio 2017 中)进行测试.但是,在无法模拟或设置用于本地测试的环境变量的情况下,我无法创建单元或集成测试.

The function works okay after publishing it with Environment variables, and it can be tested using the Lambda Function View test console (in Visual Studio 2017) after publishing it to AWS. However, I am having trouble creating unit or integration tests without being able to mock or set the environment variables for use in local testing.

有人对本地测试 Lambda 函数有什么建议或做法吗?

Does anyone have any suggestions or practices for testing the Lambda function locally?

推荐答案

这是 AWS Lambda 函数的事实是实现问题,实际上不应该与当前状态的代码难以测试这一事实有太大关系隔离中.这是设计问题.

The fact that this is AWS Lambda Function is implementation concern and really shouldn't have much bearing on the fact that the code in its current state is difficult to test in isolation. This is a matter of design issues.

考虑重构代码,使其更加灵活/可维护.

Consider refactoring the code to be a little more flexible/maintainable.

关于环境变量,考虑将静态类封装在抽象之后,以允许更松散的耦合和更好的模拟.

Concerning environment variables, consider encapsulating the static class behind an abstraction to allow for looser coupling and better mocking.

public interface ISystemEnvironment {
    string GetEnvironmentVariable(string variable);
}

public class SystemEnvironmentService : ISystemEnvironment {
    public string GetEnvironmentVariable(string variable) {
        return System.Environment.GetEnvironmentVariable(variable);
    }
}

AWSFileUploadService 在基于提供的示例的情况下与实现问题紧密耦合,存在可以利用的抽象.

The AWSFileUploadService is tightly coupling itself to implementation concerns when based on the example provided, abstractions exists that can be taken advantage of.

public class AWSFileUploadService : IFileUploadService {
    private readonly IAmazonS3 _amazonS3Client;
    private readonly TransferUtility _fileTransferUtility;

    public AWSFileUploadService(IAmazonS3 s3) {
        _amazonS3Client = s3;
        //Not sure about this next class but should consider abstracting it as well.
        _fileTransferUtility = new TransferUtility(_amazonS3Client);
    }

    public bool DoSomethingWithKinesisEvent(KinesisEvent kinesisEvent) {
        //code removed for brevity
        return true;
    }
}

根据以上两个建议,AWSLambdaFileProcessingService 现在可以重构为

With the above two suggestions the AWSLambdaFileProcessingService can now be refactored to

public class AWSLambdaFileProcessingService {
    private IFileUploadService _fileUploadService;

    [LambdaSerializer(typeof(JsonSerializer))]
    public void ProcessKinesisMessageById(KinesisEvent kinesisEvent, ILambdaContext context) {
        Console.WriteLine("Processing Kinesis Request");
        _fileUploadService = FileUploadService.Value;
        // some sort of processing
        _fileUploadService.DoSomethingWithKinesisEvent(kinesisEvent);
    }

    public static Lazy<IFileUploadService> FileUploadService = new Lazy<IFileUploadService>(() => {
        var env = new SystemEnvironmentService();
        var s3 = new AmazonS3Client(
            env.GetEnvironmentVariable("AWS_S3_KEY"),
            env.GetEnvironmentVariable("AWS_S3_SECRET_KEY")
        );
        var service = new AWSFileUploadService(s3);
        return service;
    });
}

可以在测试时根据需要替换 Lazy factory,因为它公开了一个可以在测试时模拟的抽象.

The Lazy factory can be replaced as needed when testing as it exposes an abstraction that can be mocked when testing.

以下示例使用 Moq

[TestMethod]
public void TestKinesisMessage() {
    //Arrange
    var testMessage = "59d6572f028c52057caf13ff";
    var testStream = "testStream";
    var kinesisEvent = BuildKinesisTestRequest(testMessage, testStream);
    var lambdaServiceMock = new Mock<ILambdaContext>();
    var fileUploadServiceMock = new Mock<IFileUploadService>();            
    //Replace the  lazy initialization of the service
    AWSLambdaFileProcessingService.FileUploadService = 
        new Lazy<IFileUploadService>(() => fileUploadServiceMock.Object);
    var subject = new AWSLambdaFileProcessingService();

    //Act
    subject.ProcessKinesisMessageById(kinesisEvent, lambdaServiceMock.Object);

    //Assert
    fileUploadServiceMock.Verify(_ => _.DoSomethingWithKinesisEvent(kinesisEvent), Times.AtLeastOnce());
}

事实上,通过这种设计,系统环境抽象可以被完全删除,因为它也可以被视为基于它的使用地点和方式的实现问题.

In fact, with this design the system environment abstraction could be removed altogether as it too can be considered an implementation concern based on where and how it is being used.

这篇关于AWS Lambda 环境变量和依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆