AWS Lambda环境变量和依赖注入

apl*_*roy 9 c# dependency-injection environment-variables .net-core aws-lambda

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

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

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)
    {
        // ....
    }
Run Code Online (Sandbox Code Playgroud)

```

使用环境变量发布后,该功能可以正常工作,并且可以在将其发布到AWS后使用Lambda Function View测试控制台(在Visual Studio 2017中)进行测试.但是,我无法模拟或设置环境变量以用于本地测试,因此无法创建单元测试或集成测试.

有没有人有任何关于在本地测试Lambda函数的建议或做法?

Nko*_*osi 11

这是AWS Lambda函数的事实是实现问题,并且实际上不应该对当前状态中的代码难以单独测试这一事实有太大影响.这是设计问题.

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

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

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

public class SystemEnvironmentService : ISystemEnvironment {
    public string GetEnvironmentVariable(string variable) {
        return System.Environment.GetEnvironmentVariable(variable);
    }
}
Run Code Online (Sandbox Code Playgroud)

AWSFileUploadService基于所提供的例子,当被紧密地耦合本身实施顾虑,抽象存在可被利用.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

有了上述两条建议,AWSLambdaFileProcessingService现在可以重构

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;
    });
}
Run Code Online (Sandbox Code Playgroud)

在测试时可以根据需要替换Lazy工厂,因为它暴露了可以在测试时模拟的抽象.

以下示例使用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());
}
Run Code Online (Sandbox Code Playgroud)

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

  • @apleroy包含一个简单的函数隔离单元测试示例,使用Moq验证服务是否按预期调用.可以构建更复杂的测试.这只是为了向您展示如何替换服务的Lazy初始化. (2认同)