Unit Testing and Deploying Azure Functions With DotNet Core

Azure Functions are Microsoft's serverless offering, equivalent to AWS Lambda or Google Cloud Functions. They combine event triggers with event handlers without any need to think about the hardware they are deployed on. The developer can write in C# or Javascript, which is one more language than Google, but less than AWS Lambda which adds Python and Java to the options. Strangely, for the dotnet core developer, AWS is more advanced than Microsoft who are still in beta for C# functions written in core.

Where Azure have a twist on the concept is the Function App, a container for multiple functions that can share configuration and be deployed together.

It's super-easy to create a Function App and function through the portal, simply clicking the obvious buttons in the Function App section to bring up a text editor:
function app

However, for a reliable production system we need to be able to test the function and control its deployment. Microsoft's testing documentation suggests several tactics, most of which amount to integration tests using a client like Postman. I would prefer to uphold the testing pyramid and do most of the work in the unit testing layer. This can be achieved by moving the logic to a standard class library, testable in all the usual ways, and adding a reference in the function script file. The function then simply consists of code that parses the request into library objects, passes them into the library and creates the response.

Adding a reference to a csx file
Azure C# functions are written as static functions in csx files. To add a reference to a class library, we simply create a bin folder in the function directory and add the dll to it. For dotnet core, we can either write the build output to this folder, or do a cp into it after publishing

An Example Set of Functions

My wife teaches violin. Her pupils have a lesson on a fixed day of the week, or perhaps twice weekly. Or maybe once a fortnight. I have created a set of functions to generate a schedule of dates for a given pupil's lessons during a term. Sometimes she has to cancel a lesson because she is playing a concert, so we want to be able to specify some breaks in the term. The logic for generating the lesson dates will live inside a class library, so we start by creating a dotnet core solution, a class library project and an xunit test project in our root directory.

The Function App

Before we create any functions, in order to use dotnet core we need to set the Runtime version to beta in the Function App settings. We can't change this after we have created any functions, so we do it first:
function app settings

We are going to use AppVeyor to build and deploy our functions via a zip push deployment. This requires a specific folder structure, so we create a folder specifically for the functions, I've called it Azure Functions, as you can see from the GitHub repo:
function app settings

To help setup our environment locally we first install Azure Functions Core Tools via npm. It's Node.js, so we can call it from the command line. First we create the Function App within the AzureFunctions folder:

func init TermDates  

and we have all the local config we need to write a function.

A Function

My choice is to develop the function logic in a TDD style, so I implement it in a class library, running unit tests in the usual way. I end up with a fully covered method that will generate a schedule of lessons, with the following signature:

public static Schedule Generate(Term term, IEnumerable<LessonDefinition> days, params TermBreak[] breaks)  

The Azure function will wrap this call. We create the function from the command line:

func new --language C# --template HttpTrigger --name GenerateSchedule  

The function implementation exists inside the Run method of the function .csx file. I've made this call into the class library, so first we need to copy it to the bin folder:

cp TermDates.Library/bin/Debug/netstandard2.0/TermDates.Library.dll AzureFunctions/GenerateSchedule/bin/  

then reference it in the .csx file:

#r "TermDates.Library.dll"

Now the method merely deserialises the request body into the library objects the API expects, and calls into it:

public static IActionResult Run(HttpRequest req, TraceWriter log)  
{
    log.Info("C# HTTP trigger function processed a request.");

    string requestBody = new StreamReader(req.Body).ReadToEnd();
    var request = JsonConvert.DeserializeObject<ScheduleRequest>(requestBody);

    var term = new Term(){
        Start = request.TermStart, 
        End = request.TermEnd};

    var lessonDefinitions = request.Lessons.Select( 
        l => new LessonDefinition(l.LessonDay, l.Duration, l.WeeksPerLesson ));

    log.Info(term.Start.ToLongDateString());

    var schedule = Scheduler.Generate(term, lessonDefinitions );

    return new OkObjectResult(JsonConvert.SerializeObject(schedule));
}
The CI Server

I like AppVeyor for my CI server. AppVeyor can be used to push Azure functions, as described here. Having done this, I can push to GitHub, trigger a build and instantly deploy to Azure. The end of the log will look something like this:

Synchronizing website content  
Info: Using ID '06c46681-8d0d-4c98-9aaf-97292b5e9ba0' for connections to the remote server.  
Info: Adding directory (TermDates\GetStandardTerms\bin).  
Info: Using ID '74526c2b-9e25-40d0-b887-82329d9dca81' for connections to the remote server.  
Info: Updating file (TermDates\GenerateSchedule\function.json).  
Info: Adding file (TermDates\GetStandardTerms\bin\TermDates.Library.dll).  
Info: Updating file (TermDates\GetStandardTerms\function.json).  
Info: Adding file (TermDates\GetStandardTerms\readme.md).  
Info: Updating file (TermDates\GetStandardTerms\run.csx).  
Info: Adding file (TermDates\GetStandardTerms\sample.dat).  
Total changes: 7 (4 added, 0 deleted, 3 updated, 12,374 bytes copied)  
Deployment completed  
Build success