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

查看:83
本文介绍了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 Function View测试控制台(在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 Function的事实与实现有关,实际上不应该有太大影响基于当前状态的代码很难单独进行测试。这是设计问题。

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工厂,因为它公开了一个

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天全站免登陆