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 ?
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)
{
// ....
}
```
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.
Does anyone have any suggestions or practices for testing the Lambda function locally?
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.
The
AWSFileUploadService
is tightly coupling itself to implementation concerns when based on the example provided, abstractions exists that can be taken advantage of.With the above two suggestions the
AWSLambdaFileProcessingService
can now be refactored toThe Lazy factory can be replaced as needed when testing as it exposes an abstraction that can be mocked when testing.
The following example uses Moq
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.
This answer is an attempt to implement the recommendations from @Nkosi's answer.
I am not familiar with how to override a Lazy factory and tried different methods, and the below is my attempt at an implementation method to accomplish this. The new abstraction for the Environment Variables is included below along with a new implementation of the ILambdaContext interface to accept dependencies created by the lazy factory. I post this answer in order to augment the original question and expand beyond a short comment to @Nkosi's very helpful answer.
// code start
This is the AWS Lambda function - refactored to only accept a request and pass into newly created service (where the processing logic lives)
This is a new service to encapsulate all services that act on input
This is an implementation of ILambdaContext that can also be used for testing this context allows for override of attached services in testing
This is an example of a test for the Lambda function