Azure Functions Continuous Deployment with Azure Pipelines: Part 6 - Creating an Azure DevOps Release Pipeline

This is the sixth part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

Demo app source code is available on GitHub.

In the previous instalment we saw how unit tests are executed as part of the build pipeline and that if tests fail then the build fails. If the tests (and the rest of the build) succeed, a release pipeline can be triggered to deploy the Function App automatically to Azure.

Creating an Initial Function App Release Pipeline

In your Azure DevOps project, click Pipelines and Releases.If you have no pipelines currently defined you will see a “No release pipelines found” message and a New Pipeline button.Clicking this button will start the release creation wizard, the first step of which is to choose a starter template (or start with an empty job).You should explorer these templates to get an idea for what’s possible. For the purposes of this series, click the Empty job link:

Selecting a release Azure Pipeline template

This will automatically create a stage in the new release pipeline, rename this stage to “Deploy To Production” and click the close button:

Naming a release Azre Pipeline stage

Adding Build Artifacts to a Release Pipeline

Next we need to add artifacts from the build pipeline to be used in the release. To do this click the Add link in the Artifacts section.

Leave the source type as Build (artifacts are being published from a build pipeline). As the source, select the build definition that we created earlier in this series. For the source alias enter “_InvestFunctionApp” and click Add:

Adding artifacts to an Azure release Pipeline

Adding an Azure App Service Deploy Release Task

In the “Deploy to production” stage click the “1 job, 0 task” link:

Editing Azure Pipeline release stage tasks

Click the + button next to “Agent job” to show a list of prebuilt release tasks that can be added to the stage:

Adding Azure Pipeline release stage tasks

In the search box type “app service deploy” and click the Azure App Service Deploy task that shows up, then click the Add button to add the task to the stage:

Deploying a Function App using the Azure App Service Deploy task

You will now have a new task in the stage that will state: “Some settings need attention”. Click on the newly added Azure App Service Deploy task to configure it.

First you’ll need to specify the Azure subscription you want to deploy into - you will need to click the Authorize button and go through the authorization process.

Next change “App type” to “Function App”.

From the App Service name dropdown select the Function App you want to deploy into, in this series it’s the InvestFunctionAppDemo function created earlier in this series in the Azure Portal.

The next step is to choose what to deploy. To do this use the “” next to Package or folder. This will allow you to choose the artifact that was created in the build pipeline. In this case we want to select the “app” artifact that contains the published Function App:

Selecting a deployment artifact

Select the “app” folder and click OK. You should see something similar to the following screenshot though your Azure Subscription details will be different:

Configuring a Azure App Service Deploy task

Disabling Testing Azure Functions in Production

Later in this series we’ll be adding functional end-to-end tests that make use of a number of test functions. We do not want these test functions to be enabled in the deployed production Function App in Azure. (In the next part in the series we’ll discuss these testing functions more).

To disable functions in Azure Functions V2, a setting can be added in the format “AzureWebJobs.FUNCTION_NAME.Disabled” and set the value to “true”.

One way to do this as part of the release to production is to use the Azure CLI.

To execute Azure CLI commands, you can add a new task to the stage, the Azure CLI task. This task should come before the Azure App Service Deploy task. The task can be configured as follows:

  • Display name: Disable Testing Functions
  • Azure subscription: your Azure Subscription / resource group that contains the Function App you are deploying to
  • Script location: Inline script (you could also supply a path to a source controlled script)

The contents of the inline script box are as follows:

call echo These could also be set in the Azure App Service Deploy task in the Application and Configuration Settings
call echo We are doing it this way so we ca have a separate task in the stage to make it more obvious
call az webapp config appsettings set -g DontCodeTiredDemos -n InvestFunctionAppDemo --settings AzureWebJobs.CreateInvestor.Disabled=true
call az webapp config appsettings set -g DontCodeTiredDemos -n InvestFunctionAppDemo --settings AzureWebJobs.GetInvestor.Disabled=true

This preceding script uses the az webapp config appsettings set command specifying the resource group “-g DontCodeTiredDemos”, the name of the Function App “-n InvestFunctionAppDemo” and the setting name and value “--settings AzureWebJobs.CreateInvestor.Disabled=true”.

Once configured, click the Save button and move the task above the deploy task as the following screenshot shows:

Azure release pipeline stage tasks

At this point you can also name the release pipeline by clicking “New Release Pipeline” at the top and choosing your own name, for example “InvestFunctionAppReleasePipeline”.

Enabling Continuous Deployment in an Azure Release Pipeline

If you want the release pipeline to automatically start when the build pipeline finishes, head back to the pipeline view and click the lightening bolt icon in the Artifacts area and enable the continuous deployment toggle switch as the following screenshot shows:

image

 

If you get a “Problem connecting to the service” error message it may not actually prevent the CD trigger but  you may want to refer here.

Click the Save button.

Creating a Manual Release

Click the + Release button at the top right and click Create a release.

Choose the latest build from the build pipeline in the Artifacts section and click the Create button.

You will see a message saying “Release Release-1 has been created” with a handy link to click – click this and it will take you to the release being executed:

Azure release pipeline being executed

And once the release pipeline has executed you should see a “Succeeded” message in the Deploy to Production stage.

If you head over to the Function App in the Azure Portal you should see the functions deployed, along with the two testing functions being disabled and the deployment showing for Release 1 as the following screenshot shows:

Deployed Function App from Azure Pipelines

Testing the CI Trigger

To test the CI trigger, make a change in the repository, commit the change, and then push to GitHub.

After a short while, the build pipeline should notice the change and kick off a new run of the build pipeline.

Once the build pipeline completes without error, the release pipeline should be automatically triggered and the changes (for Release 2) automatically deployed to the production Function App in Azure.

So now, we can add a feature or fix a bug, push the changes, and within a few minutes have the new feature or fix in production without us having to do anything else.

In the next instalment in this series we’ll add some functional end-to-end tests because at the moment ,all we have are unit tests in the build pipeline to verify correctness.

SHARE:

Azure Functions Continuous Deployment with Azure Pipelines: Part 5 - Adding Unit Tests

This is the fifth part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

Demo app source code is available on GitHub.

In the demo app solution there is a testing project. This project contains unit tests that can be run automatically as part of the build pipeline.

As an example, take the following function:

public static class Portfolio
{
    [FunctionName("Portfolio")]
    [return: Queue("deposit-requests")]
    public static async Task<DepositRequest> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = "portfolio/{investorId}")]HttpRequest req,
        [Table("Portfolio", InvestorType.Individual, "{investorId}")] Investor investor,
        string investorId,
        ILogger log)
    {
        log.LogInformation($"C# HTTP trigger function processed a request.");

        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

        log.LogInformation($"Request body: {requestBody}");

        var deposit = JsonConvert.DeserializeObject<Deposit>(requestBody);

        if (investor == null)
        {
            throw new ArgumentException($"Invalid investorId '{investorId}.");
        }
        if (deposit is null)
        {
            throw new ArgumentException($"Invalid deposit.");
        }
        if (deposit.Amount <= 0)
        {
            throw new ArgumentException($"Deposit amount must be greater than 1.");
        }

        // Additional validation omitted for demo purposes

        var depositRequest = new DepositRequest
        {
            Amount = deposit.Amount,
            Investor = investor
        };

        log.LogInformation($"Deposit created: {depositRequest}");

        return depositRequest;
    }
}

The function Run method has a number of parameters that need to be satisfied when executed: HttpRequest, Investor, investorId, and an ILogger. The ILogger and HttpRequest can be mocked using Moq:

public class AddToPortfolioShould
{
    [Fact]
    public async Task ReturnCorrectDepositInformation()
    {
        var deposit = new Deposit { Amount = 42 };
        var investor = new Investor { };

        Mock<HttpRequest> mockRequest = CreateMockRequest(deposit);

        DepositRequest result = await Portfolio.Run(mockRequest.Object, investor, "42", new Mock<ILogger>().Object);

        Assert.Equal(42, result.Amount);
        Assert.Same(investor, result.Investor);
    }

    [Fact]
    public async Task ErrorWhenInvestorDoesNotExist()
    {
        var deposit = new Deposit { Amount = 42 };

        Mock<HttpRequest> mockRequest = CreateMockRequest(deposit);

        await Assert.ThrowsAsync<ArgumentException>(() => Portfolio.Run(mockRequest.Object, null, "42", new Mock<ILogger>().Object));
    }

    private static Mock<HttpRequest> CreateMockRequest(object body)
    {            
        var ms = new MemoryStream();
        var sw = new StreamWriter(ms);

        var json = JsonConvert.SerializeObject(body);

        sw.Write(json);
        sw.Flush();

        ms.Position = 0;

        var mockRequest = new Mock<HttpRequest>();
        mockRequest.Setup(x => x.Body).Returns(ms);

        return mockRequest;
    }
}

Side note: In the preceding code, the CreateMockRequest method makes use of .NET streams, if you’re not familiar with streams or want to understand them better, check out my Working with Files and Streams in C# Pluralsight course.

There are also CalculatePortfolioAllocationShould and NaiveInvestementAllocatorShould test classes.

Executing .Net Core Unit Tests in an Azure Pipeline

Recall from earlier in this series that we defined the build using a YAML file.

There are a couple of steps that are related to unit tests.

The first is to execute dotnet test:

- script: dotnet test 'src/InvestFunctionApp/InvestFunctionApp.Tests' --configuration $(buildConfiguration) --logger trx
  displayName: 'Run unit tests'

Notice in the preceding YAML that the test project being executed is “src/InvestFunctionApp/InvestFunctionApp.Tests” and the trx option is being specified to log test results into a Visual Studio Test Results File (TRX) format file.

Once the tests execute, the results need to be made available to the pipeline  so we can see/explore the test results in the GUI. To do this the PublishTestResults@2 task can be used:

- task: PublishTestResults@2
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

If the test(s) fail the build will fail and the release pipeline won’t execute and try and deploy a build with failing tests.

Once a pipeline build job has executed, the test results can be viewed by clicking on the Tests tab for a specific build job as the following screenshot shows:

Viewing Unit Test Result in Azure Pipelines

Note that in this series we’re using xUnit.net as the testing framework and Moq as the mocking library but you could use MSTest or NUnit and a different mocking library if you wish. If you want to learn more about  xUnit.net or Moq, check out my Pluralsight courses: Testing .NET Core Code with xUnit.net: Getting Started and Mocking in .NET Core Unit Tests with Moq: Getting Started.

In the next part of this series, now that we have a build pipeline that will run tests and create artifacts, we can create a release pipeline to automatically deploy the app to production if the build succeeds.

SHARE:

Azure Functions Continuous Deployment with Azure Pipelines: Part 4 - Defining and Consuming Pipeline Variables

This is the fourth part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

Demo app source code is available on GitHub.

In the previous instalment we created a build pipeline, defined the build steps using YAML, but when we tried to execute the build, it failed with the error:”buildConfiguration: command not found”. This is because in the YAML build definition we are referencing the $(buildConfiguration) variable which has not  yet been defined.

An Overview of Azure Pipeline Build Variables

“Variables give you a convenient way to get key bits of data into various parts of your build pipeline.” [Microsoft]

Essentially variables allow you to define key/value pairs that can be used throughout the pipeline. They can be passed to YAML build definitions for use in steps, they can be accessed as environment variables in scrips and from unit tests, and can be used in the pipeline GUI when configuring tasks.

There are a whole host of predefined variables such as Agent.JobStatus, Build.BuildNumber, System.PullRequest.PullRequestId, etc.

You can also create your own custom variables.

Creating a Custom Variable in an Azure Pipeline

First off, open the build pipeline by clicking Builds, select the build pipeline you want to edit, and click the Edit button:

Editing an Azure Pipeline build

In the Variables section click Add:

Creating a new Azure Pipeline variable

For the variable Name enter “buildConfiguration” and for the variable Value enter “Release”, also check the “Settable at queue time check box”. This variable will default to “Release” but can also be set to “Debug” when manually queuing a build if you want to create a debug version of the Function App:

Setting an Azure Pipeline variable

Notice the little padlock to the right of the value, clicking this enables you to store “sensitive values in a way that they cannot be seen or changed by users of the release pipelines” [Microsoft]

“The values of hidden (secret) variables are stored securely on the server and cannot be viewed by users after they are saved. During a deployment, the Azure Pipelines release service decrypts these values when referenced by the tasks and passes them to the agent over a secure HTTPS channel.” [Microsoft]

Another nice thing about designating a value as sensitive is that it will also be redacted in log messages produced by Azure Pipelines. Sensitive variables can also be passed to scripts – you should read the documentation and be careful about any custom logging or other output you create as you may inadvertently reveal secrets.

To save the changes, click the Save & queue dropdown and choose Save:

Saving changes to and Azure Pipeline

Once defined, build variables can be consumed in the YAML build template by using the token pattern $(VARIABLENAME), for example $(buildConfiguration):

- script: dotnet build 'src/InvestFunctionApp/InvestFunctionApp.sln' --configuration $(buildConfiguration)
  displayName: 'Build solution'

Manually Queuing an Azure Pipeline Build

To queue up a new build, click the Queue button, optionally choose a branch/commit and click Queue. (Also notice here that it’s possible to set the buildConfiguration variable to a custom value for this build or leave it as the default “Release”):

Manually Queuing an Azure Pipeline Build

Click the handy shortcut in the notification:

Manually Queuing an Azure Pipeline Build

And this will take you to the build that should start executing.

Now that we have the custom variable defined, the build should now complete successfully:

Successfull Azure Pipeline build

Also notice that the steps in the build from “Build solution” to “Publish end to end test artifact” correspond to the steps defined in the YAML and that these are set using displayName: 'Run unit tests'.

In the next instalment of this series we’ll look at how unit tests are executed as part of the build pipeline.

SHARE:

Azure Functions Continuous Deployment with Azure Pipelines: Part 3 - Creating an Initial Build Pipeline

This is the third part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

Demo app source code is available on GitHub.

In the previous instalment we created an Azure DevOps organization and project, we can now create a build pipeline by clicking the New pipeline button:

Starting to create a new build Azure Pipeline

Next we need to specify where the source code is located, this could be hosted inside Azure DevOps using Azure Repos or GitHub. The demo app is located in GitHub.

Triggering an Azure Pipeline from GitHub

Next we need to give permission from GitHub to enable the pipeline to access the repository. The easiest way to do this is to click the Install our app from the GitHub Marketplace link as shown in the following screenshot:

Installing the Azure Pipelines GitHub App

You can allow access to all current (and future) repositories or specified ones:

Installing the Azure Pipelines GitHub App

Click the Install button, confirm your GitHub password, and authorize Azure Pipelines:

Authorize Azure Pipelines

You will be redirected back to Azure DevOps where you can now select the InvestFunctionApp GitHub repository.

You can now select a starter YAML template that will define what happens during the build:

Defining a starter YAML Azure Pipeline build template

Choose the ASP.NET Core template:

Defining a starter YAML Azure Pipeline build template

And click Save and run and then Save and run again.

The build job will now run:

Initial build pipeline executing

Initial build pipeline executing

Initial build pipeline executing

This initial build will fail but don’t worry as we need to go and edit the YAML.

Defining an Azure Pipeline Build for an Azure Function App in YAML

When we chose the starter template earlier, a new file was added to the root of the repository in GitHub called azure-pipelines.yml.

This file defines the steps that make up the build using YAML schema.

Essentially there are a number of steps in a build, each step could be a handwritten custom script, calling a prebuilt task, or referencing another template.

We can  now customize the YAML to defined the build as follows:

pool:
  vmImage: 'Ubuntu 16.04'
  
steps:

- script: dotnet build 'src/InvestFunctionApp/InvestFunctionApp.sln' --configuration $(buildConfiguration)
  displayName: 'Build solution'
    
- script: dotnet test 'src/InvestFunctionApp/InvestFunctionApp.Tests' --configuration $(buildConfiguration) --logger trx
  displayName: 'Run unit tests'

- task: PublishTestResults@2
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

- script: dotnet publish 'src/InvestFunctionApp/InvestFunctionApp/InvestFunctionApp.csproj' --configuration $(buildConfiguration) --output '$(Build.ArtifactStagingDirectory)/app'
  displayName: 'Package function app'

- task: PublishBuildArtifacts@1
  displayName: 'Publishing app artifact'
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)/app'
    artifactName: app

- task: CopyFiles@2
  displayName: 'Copy end to end tests'
  inputs:
    sourceFolder: 'src/InvestFunctionApp'
    targetFolder: '$(Build.ArtifactStagingDirectory)/e2etests'

- task: PublishBuildArtifacts@1
  displayName: 'Publish end to end test artifact'
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)/e2etests'
    artifactName: e2etests

In the preceding YAML, the displayName items help to describe what is happening and are hopefully fairly descriptive (they will also appear in the Azure Pipeline GUI/logs when builds are run).

Creating Multiple Build Artifacts in Azure Pipelines

One important thing to note in the preceding YAML, is that we are creating two separate build artifacts, one that contains only the Function App contents (for deployment to Azure) and one to be able to run the tests.

The Function App artifact is created by first calling dotnet publish and choosing the (temporary) output directory app with the switch --output '$(Build.ArtifactStagingDirectory)/app'. To actually create the build artifact that can be consumed in a release pipeline, the prebuilt PublishBuildArtifacts@1 task is called. This task takes its input from pathtoPublish: '$(Build.ArtifactStagingDirectory)/app' and will create a build artifact with the name app by virtue of the artifactName: app setting.

To create a separate artifact for the tests, the CopyFiles@2 task is used to copy (effectively the entire solution including the test projects) to the (temporary) targetFolder: '$(Build.ArtifactStagingDirectory)/e2etests'. The final PublishBuildArtifacts@1 task creates a second build artifact called e2etests.

We’ll see these artifacts used later in this series of blog posts.

The reason we create two artifacts here is to separate out what will get deployed to Azure Functions from  the code/binaries that contain the tests. What we don’t want is to publish the tests, xUnit.net DLLs etc. to the deployed function apps in Azure. This can also help the in readability of the release pipeline and potentially help to segregate things so that only the indented things get deployed.

We can now save the changes to azure-pipelines.yml  and push them to GitHub (which will actually trigger a new CI build).

Viewing Azure Pipeline Builds

Once the changes are pushed, the Azure Pipeline that we created will notice the changes and execute. If you click on Builds you will see the build running:

Azure Pipeline Build Running

Clicking in the build will show you all the steps:

Azure Pipeline build details

Notice in the preceding screenshot that the “Build solution” step is failing. Clicking it will show you the logs for the step:

Viewing Azure Pipeline build logs

Notice in the preceding screenshot the error”buildConfiguration: command not found”. This is because in the YAML build definition we are referencing the $(buildConfiguration) variable which is not yet defined.

In the next instalment of this series we’ll learn how to add variables to a pipeline and fix this problem.

SHARE:

Azure Functions Continuous Deployment with Azure Pipelines: Part 2 - Getting Started

This is the second part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

In the previous instalment we got an overview  of Azure DevOps and of the app we’ll be building/deploying.

Demo app source code is available on GitHub.

Creating Azure Function Apps

There will be two Function Apps created in Azure. One will be a self-contained test environment (with separate Azure Storage account) and to keep things simple they both belong to the same resource group. The other Function App will be the production version. For simplicity we won’t be using slots, proxies, etc.

The two Function Apps are:

  • InvestFunctionAppDemo (production)
  • InvestFunctionAppDemoTest (test)

The test environment will be used to check the deployment  artifacts and as a target to execute functional end-to-end tests against.

Once these two Function Aps have been created in the Portal, all of the deployments will happen automatically from the release pipeline, including setting test/production specific Function App settings.

If you don’t have an Azure account you can currently sign up for free.

Signing Up or Azure DevOps

Now that there are some Function Apps created in Azure to deploy to, you can deploy to them from an Azure release pipelines. To do this you’ll need to sign up to Azure DevOps.

Azure DevOps contains a number of services, in this series we’ll be using the Azure Pipeline service.

An Overview of Azure Pipelines

Azure DevOps is currently free for open source projects and small teams with some limitations on things such as parallel job execution all the way up to 1,000 users for $7,833.26 USD/month. You can find the latest pricing information as part of the product information and compare features of different plans.

You can just use Azure Pipelines on their own, you don’t have to use all of the DevOps services (Boards, etc.).

Some of the features of Azure Pipelines include:

  • Build, test, and deploy for multiple languages including .NET. Python, Java, iOS, etc.
  • Container build and publish support
  • Deploy to clouds including Azure, AWS, and Google Cloud Platform
  • 10 free parallel jobs and unlimited build minutes for all open source projects
  • Advanced workflow features, testing, reporting, gates, etc.

Azure DevOps Organizations and Projects

Once you’ve signed up for Azure DevOps you’ll need an organization and a project.

“..an organization is a mechanism for organizing and connecting groups of related projects. Examples are business divisions, regional divisions, or other organizational structure. You can choose one organization for your entire company, or separate organizations for specific business units, or an organization just for you.” [Microsoft]

“Each organization contains one or more projects. Each project contains a set of features: boards and backlogs for agile planning, pipelines for continuous integration and deployment, repos for version control and management of source code and artifacts, and continuous test integration throughout the life cycle.” [Microsoft]

Creating a new Azure DevOps Project

Once the project is created, you can navigate to Pipelines section to create and manage build and release pipelines.

In the next part of this series we’ll create an initial build pipeline and get an introduction to defining source-controlled build definitions using YAML.

SHARE:

Azure Functions Continuous Deployment with Azure Pipelines: Part 1 - Overview

This is the first part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

In this series we’ll explore (among other things) how to:

  • Build an Azure Functions App when code is pushed to GitHub
  • Run unit tests as part of the build
  • Create multiple Azure Pipeline artifacts
  • Release to a test Function App environment in Azure
  • Run automated functional end to end tests
  • Release to a production Function App environment in Azure
  • Run smoke tests after a production deployment

…in addition to a whole host of other things such as: YAML build files, Azure Pipeline variables, setting Azure Function app settings from an Azure release pipeline, and using post-deployment gates.

An Overview of Azure DevOps

Azure DevOps is a collection of cloud services to facilitate team DevOps practices and includes:

  • Azure boards
  • Azure Pipelines
  • Azure repos
  • Azure test plans
  • Azure Artifacts

In this series we’ll be focussing on Azure Pipelines to build, test, and deploy the Function App automatically when new changes are pushed to GitHub.

InvestFunctionApp Overview

The demo code for these demos can be found on GitHub.

The InvestFunctionApp contains a number of functions that allow the investing of money in a portfolio. The initial entry point is a HTTP-triggered function that allows the investor id to be specified along with how much to add to the investors portfolio.Depending on the investors target allocations, the amount will be investing in either bonds or shares.

There are 4 functions involved in this workflow:

  1. Portfolio function: HTTP trigger, starts the workflow, creates a queue message
  2. CalculatePortfolioAllocation function: queue trigger from the output of (1), calculates where to invest and writes to either buy-stocks queue (3) or buy-bonds queue (4)
  3. BuyStocks function: triggered from buy-stocks queue, simulates buying stocks and updates the investor’s portfolio in Azure Table storage
  4. BuyBonds function: triggered from buy-bonds queue, simulates buying bonds and updates the investor’s portfolio in Azure Table storage

The app is designed to demonstrate the  facilities of Azure Pipelines and is simplified in many ways including minimal error checking, auditing, transactions, security, etc.

Azure DevOps Build Pipeline

The build Azure Pipeline will be automatically triggered from changes pushed to the GitHub repository.

The build pipeline will have the following steps:

  1. Build the solution
  2. Run unit tests
  3. Publish test results
  4. Package the Function App
  5. Publish the packaged Function App as a build artifact
  6. Copy the functional end-to-end tests
  7. Publish the functional end-to-end tests as a separate build artifact

Assuming all these step run without error, the two build artifacts can be passed to/used by the release pipeline.

Azure DevOps Release Pipeline

The release pipeline will be triggered automatically when the build pipeline completes without error.

The following diagram shows what the resulting release pipeline will look like:

Azure DevOps Release Pipeline example

The release pipeline will:

  1. Deploy the Function App to a test Function App in Azure
  2. Run functional end-to-end tests (written in xUnit.net) against this test deployment
  3. If the tests in (2) pass, deploy to production
  4. After deploying to production, call a smoke test function to verify the deployment was successful

In the next part of this series we’ll create Function Apps, signup to Azure DevOps, create a new DevOps project, and take a closer look at the demo function app.

SHARE:

Mocking HttpRequest Body Content When Testing Azure Function HTTP Trigger Functions

When creating Azure Functions that are triggered by an HTTP request, you may want to write unit tests for the function Run method. These unit tests can be executed outside of the Azure Functions runtime, just like testing regular methods.

If your HTTP-triggered function takes as the function input an HttpRequest (as opposed to an automatically JSON-deserialized class) you may need to provide request data in your test.

As an example, consider the following code snippet that defines an HTTP-triggered function.

[FunctionName("Portfolio")]
[return: Queue("deposit-requests")]
public static async Task<DepositRequest> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "portfolio/{investorId}")]HttpRequest req,
    [Table("Portfolio", InvestorType.Individual, "{investorId}")] Investor investor,
    string investorId,
    ILogger log)
{
    log.LogInformation($"C# HTTP trigger function processed a request.");

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

    log.LogInformation($"Request body: {requestBody}");

    var deposit = JsonConvert.DeserializeObject<Deposit>(requestBody);

    // etc.
}

If the  the preceding code is executed in a test, some content needs to be provided to be used when accessing req.Body. To do this using Moq a mock HttpRequest can be created that returns a specified Stream instance for req.Body.

If you want to create a request body that contains a JSON payload, you can use the following helper method in your tests:

private static Mock<HttpRequest> CreateMockRequest(object body)
{            
    var ms = new MemoryStream();
    var sw = new StreamWriter(ms);

    var json = JsonConvert.SerializeObject(body);

    sw.Write(json);
    sw.Flush();

    ms.Position = 0;

    var mockRequest = new Mock<HttpRequest>();
    mockRequest.Setup(x => x.Body).Returns(ms);

    return mockRequest;
}

As an example of using this method in a test:

[Fact]
public async Task ReturnCorrectDepositInformation()
{
    var deposit = new Deposit { Amount = 42 };
    var investor = new Investor { };

    Mock<HttpRequest> mockRequest = CreateMockRequest(deposit);

    DepositRequest result = await Portfolio.Run(mockRequest.Object, investor, "42", new Mock<ILogger>().Object);

    Assert.Equal(42, result.Amount);
    Assert.Same(investor, result.Investor);
}

When the preceding test is run, the function run method will get the contents of the memory stream that contains the JSON.

To learn more about using Moq to create/configure/use mock objects check out my Mocking in .NET Core Unit Tests with Moq: Getting Started Pluralsight course. or to learn more about MemoryStream and how to work with streams in C# check out my Working with Files and Streams course.

SHARE:

Remote Debugging Azure Functions V2 "The breakpoint will not currently be hit. No symbols have been loaded for this document"

Sometimes it can be tricky to attach the Visual Studio debugger to a deployed Azure Functions app. For example if you use Cloud Explorer you can right click on the deployed Azure Function and choose Attach Debugger as the following screenshot shows:

Using Cloud Explorer to debug an Azure Function

While this may seem to work at first, you may experience a problem with your breakpoints actually being hit when function app code is executing with the message “The breakpoint will not currently be hit. No symbols have been loaded for this document”.

The breakpoint will not currently be hit. No symbols have been loaded for this document

As an alternative to attaching via Cloud Explorer, you can try the following approach:

1 Log in to Azure Portal and navigate to your deployed function app.

2 Download the publish profile

Downloading publish profile for Azure Function app

3 Open the downloaded file

Make a note of the userName, userPWD and destinationAppUrl values.

4 Attach Visual Studio Debugger to Azure Function App

  1. Make sure your function app project is open in Visual Studio
  2. Make sure that you have deployed a debug version of the function app to Azure
  3. On the Debug menu choose Attach to Process..
  4. For the Attach to value, click the Select.. button and un-tick everything except Managed (CoreCLR) code
  5. In the Connection target enter the  destinationAppUrl (without the preceding http) followed by :4022 – for example: investfunctionappdemotest.azurewebsites.net:4022 – and hit Enter
  6. You should now see an Enter Your Credentials popup box, use the userName and userPWD from step 3 and click Ok
  7. Wait a few seconds for Visual Studio to do its thing
  8. Click the w3wp.exe process and the click the Attach button (see screenshot below) Be patient after clicking as it may take quite a while for all the debug symbols to load.

Attaching to the Azure Functions w3wp.exe process

5 Set your breakpoints as desired

Breakpoint set in function run method

6 Invoke your function code and you should see your breakpoint hit

Once again this may take a while so be patient, you may also see “a remote operation is taking longer than expected”.

Azure Function breakpoint being hit in Visual Studio

SHARE:

Azure Functions Dependency Injection with Autofac

This post refers specifically to Azure Function V2.

If you want to write automated tests for Azure Functions methods and want to be able to control dependencies (e.g. to inject mock versions of things) you can set up dependency injection.

One way to do this is to install the AzureFunctions.Autofac NuGet package into your functions project.

Once installed, this package allows you to inject dependencies into your function methods at runtime.

Step 1: Create DI Mappings

The first step (after package installation) is to create a class that configures the dependencies. As an example suppose there was a function method that needed to make use of an implementation of an IInvestementAllocator. The following class can be added to the functions project:

using Autofac;
using AzureFunctions.Autofac.Configuration;

namespace InvestFunctionApp
{
    public class DIConfig
    {
        public DIConfig(string functionName)
        {
            DependencyInjection.Initialize(builder =>
            {
                builder.RegisterType<NaiveInvestementAllocator>().As<IInvestementAllocator>(); // Naive

            }, functionName);
        }
    }
}

In the preceding code, a constructor is defined that receives the name of the function that’s being injected into. Inside the constructor, types can be registered for dependency injection. In the preceding code the IInvestementAllocator interface is being mapped to the concrete class NaiveInvestementAllocator.

Step 2: Decorate Function Method Parameters

Now the DI registrations have been configured, the registered types can be injected in function methods. To do this the [Inject] attribute is applied to one or more parameters as the following code demonstrates:

[FunctionName("CalculatePortfolioAllocation")]
public static void Run(
    [QueueTrigger("deposit-requests")]DepositRequest depositRequest,
    [Inject] IInvestementAllocator investementAllocator,
    ILogger log)
    {
        log.LogInformation($"C# Queue trigger function processed: {depositRequest}");

        InvestementAllocation r = investementAllocator.Calculate(depositRequest.Amount, depositRequest.Investor);
    }

Notice in the preceding code the [Inject] attribute is applied to the IInvestementAllocator investementAllocator parameter. This IInvestementAllocator is the same interface that was registered earlier in the DIConfig class.

Step 3: Select DI Configuration

The final step to make all this work is to add an attribute to the class that contains the function method (that uses [Inject]). The attribute used is the DependencyInjectionConfig attribute that takes the type containing the DI configuration as a parameter, for example: [DependencyInjectionConfig(typeof(DIConfig))]

The full function code is as follows:

using AzureFunctions.Autofac;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace InvestFunctionApp
{
    [DependencyInjectionConfig(typeof(DIConfig))]
    public static class CalculatePortfolioAllocation
    {
        [FunctionName("CalculatePortfolioAllocation")]
        public static void Run(
            [QueueTrigger("deposit-requests")]DepositRequest depositRequest,
            [Inject] IInvestementAllocator investementAllocator,
            ILogger log)
        {
            log.LogInformation($"C# Queue trigger function processed: {depositRequest}");

            InvestementAllocation r = investementAllocator.Calculate(depositRequest.Amount, depositRequest.Investor);
        }
    }
}

At runtime, when the CalculatePortfolioAllocation runs, an instance of an NaiveInvestementAllocator will be supplied to the function.

The library also supports features such as named dependencies and multiple DI configurations, to read more check out GitHub.

SHARE:

Setting Up Mock ref Return Values in Moq

I recently received a message related to my Mocking in .NET Core Unit Tests with Moq: Getting Started Pluralsight course asking how to set the values of ref parameters.

As a (somewhat contrived) example, consider the following code:

public interface IParser
{
    bool TryParse(string value, ref int output);
}

public class Thing
{
    private readonly IParser _parser;

    public Thing(IParser parser)
    {
        _parser = parser;
    }

    public string ConvertStringIntToHex(string number)
    {
        int i = 0;

        if (_parser.TryParse(number, ref i))
        {
            return i.ToString("X");
        }

        throw new ArgumentException("The value supplied cannot be parsed into an int.", nameof(number));
    }
}

The Thing class requires an IParser to be able to work. In a test, a mocked version of an IParser can be created by Moq as the following initial test demonstrates:

[Fact]
public void ReturnHex_Fail_NoSetup()
{
    var mockParser = new Mock<IParser>();

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255"); // fails with ArgumentException

    Assert.Equal("FF", result);
}

The preceding test will fail however because the mocked TryParse has not been configured correctly, for example specifying that the method should return true.

The following modified test attempts to fix this:

[Fact]
public void ReturnHex_Fail_NoRefValueSetup()
{
    var mockParser = new Mock<IParser>();
    mockParser.Setup(x => x.TryParse(It.IsAny<string>(), ref It.Ref<int>.IsAny))
              .Returns(true);

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255");

    Assert.Equal("FF", result); // Fails, actual result == 0
}

In the preceding code, the return value is being set, but nowhere is the ref int output “return value” being configured.

In the following test the Callback method is used to set the ref value. To be able to do this, a delegate must first be defined that matches the signature of the mocked method that contains the ref parameter. Once this delegate is defined it can be used in the Callback method as the following code demonstrates:

// Define a delegate that can be used to set the ref value in the mocked TryParse method 
delegate void MockTryParseCallback(string number, ref int output);

[Fact]
public void ReturnHex()
{
    var mockParser = new Mock<IParser>();
    mockParser.Setup(x => x.TryParse("255", ref It.Ref<int>.IsAny)) // When the TryParse method is called with 255
              .Callback(new MockTryParseCallback((string s, ref int output) => output = 255)) // Execute callback delegate and set the ref value
              .Returns(true); // Return true as the result of the TryParse method

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255");

    Assert.Equal("FF", result);
}

If you’ve never used Moq or want to learn more about it check out the official Moq quickstart  or head over to my Pluralsight course.

SHARE: