Pretty Method Display in xUnit.net

One little-known feature of the xUnit.net testing framework is the ability to write test method names in a specific way and then have them converted to a ‘pretty’ version for example in Visual Studio Test Explorer.

Take the following test method:

using ClassLibrary1;
using Xunit;

namespace XUnitTestProject2
{
    public class CalculatorShould
    {
        [Fact]
        public void Add2PositiveNumbers()
        {
            var sut = new Calculator();

            sut.Add(1);
            sut.Add(1);

            Assert.Equal(2, sut.Value);
        }
    }
}

By default, this will look like the following screenshot in Visual Studio Test Explorer:

Default xUnit.net Test Method Name Display

The first thing that can be modified to to simplify the test method name display to only display the test method name and not the preceding namespace and class name, for example “XUnitTestProject2.CalculatorShould.Add2PositiveNumbers” becomes more simply “Add2PositiveNumbers” by making a simple configuration change.

Displaying Only Test Method Names in xUnit.net Tests

To control the rendering of method names in xUnit.net, the first thing to do is add a new file called “xunit.runner.json” to the root of the test project and set the Copy To Output Directory property to Copy if newer. This will make this file copy to the output bin directory. Once this is done, if you open the project file you should see something like:

<ItemGroup>
  <None Update="xunit.runner.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Next, modify the json file to the following:

{
  "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
   "methodDisplay": "method"
}

Notice in the preceding json configuration the methodDisplay has been set to “method”, this will prevent the namespace and class being prepended to the method name in Test Explorer.

Now if you head back to Test Explorer you should see the following:

Method name only display in xUnit.net tests

Enabling Pretty Method Names in xUnit.net

In addition to shortening test method name display we can also make use of xUnit.net’s “pretty method display”.

To enable this feature, modify the json configuration file and add the "methodDisplayOptions": "all" configuration as follows:

{
  "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
  "methodDisplay": "method",
  "methodDisplayOptions": "all"
}

Now the previous test can be renamed to “Add_2_positive_numbers” as follows:

[Fact]
public void Add_2_positive_numbers()
{
    var sut = new Calculator();

    sut.Add(1);
    sut.Add(1);

    Assert.Equal(2, sut.Value);
}

In test explorer this test method will show up as “Add 2 positive numbers” as the following screenshot shows:

xUnit.net pretty method display names

You can use other items in the test method name, for example you can use the monikers eq, ne, lt, le, gt, ge that get replaced with =, !=, <, <=, >, >= respectively, for example a test name of “Have_a_value_eq_0_when_multiplied_by_zero” would be displayed as “Have a value = 0 when multiplied by zero”. Here the eq has been replaced with =.

You can also use ASCII or Unicode escape sequences, for example the test name “Divide_by_U00BD” gets displayed as “Divide by ½” and the test “Email_address_should_only_contain_a_single_U0040” gets displayed as “Email address should only contain a single @”, or “The_U2211_of_1U002C_2_and_3_should_be_6” becomes “The ∑ of 1, 2 and 3 should be 6”:

xUnit Pretty methods

You could also combine the "methodDisplay": "classAndMethod" to create something like  and the following:

namespace Given_a_cleared_calculator
{
    public class when_a_number_gt_0_is_added
    {
        [Fact]
        public void then_the_value_should_be_gt_0()
        {
            // etc.
        }

        [Fact]
        public void then_the_value_should_eq_the_one_added()
        {
            // etc.
        }
    }
}

This would produce the following tests in Test Explorer:

xUnit.net Pretty Display Names

If you want to learn more about writing tests with xUnit.net check out my Pluralsight course today.

SHARE:

Simplifying Parameter Null and Other Checks with the Pitcher Library

In a previous post I looked at the GaurdClauses library that can simplify the usual guard checks we sometimes need to write. In the comments someone mentioned the Pitcher library that accomplishes the same thing so I thought I’d check it out here.

First, the NuGet package needs to be installed and a using Pitcher; directive added, then we can make use of the library.

As an example, without the library you might end up with some code like the following:

public static void AddNewPerson(string name, int ageInYears)
{
    if (string.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentException($"Cannot be null, empty, or contain only whitespace.", nameof(name));
    }

    if (ageInYears < 1)
    {
        throw new ArgumentOutOfRangeException(nameof(ageInYears), "Must be greater than zero.");
    }

    // Add to database etc.
}

This is just some boilerplate type code to check for null/empty strings and that the age that’s passed in is positive.

With the Pitcher library this could be refactored to the following (I’ve left the GaurdClauses code commented as a comparison):

public static void AddNewPerson(string name, int ageInYears)
{
    // GuardClauses version:
    // Guard.Against.NullOrWhiteSpace(name, nameof(name));
    // Guard.Against.NegativeOrZero(ageInYears, nameof(ageInYears));


    // Pitcher version:
    Throw.ArgumentNull.WhenNullOrWhiteSpace(name, nameof(name));

    Throw.ArgumentOutOfRange.WhenLessThan(ageInYears, mustBeMoreThan:0, nameof(ageInYears));
    // or more simply:
    Throw.ArgumentOutOfRange.WhenNegativeNumber(ageInYears, nameof(ageInYears));            
}

Pitcher allows you to throw either an ArgumentOutOfRangeException or an ArgumentNullException when using this syntax and also provides ways to throw other exception types when a given condition is true, for example:

Throw.When(ageInYears == 42, new InvalidOperationException("This age has no meaning"));

You can find the source code and more examples on GitHub and don’t forget to say hi/thanks to the project maintainer Alex Kamsteeg and tell him you heard about Pitcher here :)

SHARE:

New Pluralsight Course: Creating Automated Browser Tests with Selenium in C#

My newest Pluralsight course was just published and you can start watching today. Selenium is a tool that allows you to automate a web browser and simulate an end-user interacting with your web app. You can combine Selenium with a test framework such as xUnit.net to create tests that check your web app is working as expected.

Automated browser tests can compliment your other types of tests such as unit and integration tests.

From the course description: “Unit and integration tests can help you catch a range of bugs, but not all of them. Even if your unit and integration tests pass, you could still deploy your web app to production and find it doesn’t work as expected. In this course, Creating Automated Browser Tests with Selenium in C#, you will gain the ability to create tests that automate the browser and simulate a real person using your web app. First, you will learn how to set up your test project and write your first test. Next, you will discover how to interact with web page elements from your tests, such as clicking a button or typing text. Finally, you will explore how to create a suite of automated web tests that are easier to maintain over time. When you are finished with this course, you will have the skills and knowledge of Selenium automated browser testing needed to help ensure your web app is working as expected before you release it to production.”

Check out the course today and if you’re not a Pluralsight member you can currently start watching for free with a Pluralsight Free Trial with Unlimited Access .

SHARE:

Adding Tuple Support to .NET Classes in C#

Edit: Updated to improve clarity (thanks to Paulo in the comments for helping to improve his article).

Tuples in C# are objects that can be created with a specific syntax. You don’t have to declare tuple types first like you do with classes for example, they can instead be created using a lightweight C# syntax.

A tuple is a object that holds a number of arbitrary data items and which has no custom behaviour. In contrast, a class or struct can have both data and custom behaviour.

For example the following creates a tuple with 2 string values:

(string, string) names = ("Sarah", "Smith");
Console.WriteLine($"First name: '{names.Item1}' Last name: '{names.Item2}'");

This code produces the output: First name: 'Sarah' Last name: 'Smith'

In the preceding code, the items inside the tuple don’t have names so they are referred to as Item1 and Item2 but you could also name the items, for example:

(string firstName, string lastName) names = ("Sarah", "Smith");
Console.WriteLine($"First name: '{names.firstName}' Last name: '{names.lastName}'");

If you had a rich Person class that had both data and behaviour, you could also add support for tuple-like deconstruction and unpackaging of a Person instance into variables just like you would do with a tuple instance.

Consider the following class:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AgeInYears { get; set; }
    public string FavoriteColor { get; set; }
    
    // methods etc.
}

We could create a tuple as before containing the first and last name as follows:

var sarah = new Person
{
    FirstName = "Sarah",
    LastName = "Smith",
    AgeInYears = 42,
    FavoriteColor = "red"
};

(string firstName, string lastName) names = (sarah.FirstName, sarah.LastName);
Console.WriteLine($"First name: '{names.firstName}' Last name: '{names.lastName}'");

This is however a little clunky, we can modify the Person class to provide support for a Person to have tuple-like deconstruction and unpacking semantics. To do this a public void method called Deconstruct can be added, for example:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AgeInYears { get; set; }
    public string FavoriteColor { get; set; }

    // methods etc.

    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

Now the code could be changed to:

var (firstName, lastName) = sarah;
Console.WriteLine($"First name: '{firstName}' Last name: '{lastName}'");

You could also add this deconstruction/unpackaging support to a class you can’t change by declaring an extension method such as:

static class PersonExtensions
{
    public static void Deconstruct(this Person person, out string firstName, out string lastName)
    {
        firstName = person.FirstName;
        lastName = person.LastName;
    }
}

Or as another example, you could add tuple-like deconstruction & unpackaging support for the .NET String type:

static class StringExtensions
{
    public static void Deconstruct(this string s, out string original, out string upper, out string lower, out int length)
    {
        original = s;
        upper = s.ToUpperInvariant();
        lower = s.ToLowerInvariant();
        length = s.Length;
    }
}

And then write:

var (original, upper, lower, length) = "The quick brown fox";
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Uppercase: {upper}");
Console.WriteLine($"Lowercase: {lower}");
Console.WriteLine($"Length: {length}");

As Paulo points out in the comments there is no actual tuple instance per-se involved here, if look at the the decompiled source that Paulo links to you can see the Person has been unpackaged into multiple variables.

If you want to learn a load more C# tips check out my C# Tips and Traps course today. You can even currently start watching with a Pluralsight Free Trial with Unlimited Access .

SHARE:

Variables? We Don’t Need No Stinking Variables - C# Discards

C# 7.0 introduced the concept of discards. Discards are intentionally unused, temporarily dummy variables that we don’t care about and don’t want to use.

For example, the following shows the result of an addition being discarded:

_ = 1 + 1;

Note the underscore _ this is the discard character.

Given the preceding example, you cannot access the result of this addition, for example:

WriteLine(_); // Error CS0103  The name '_' does not exist in the current context 

Using C# Discards with Out Parameters

A more useful example is when you are working with a method that has one or more out parameters and you don’t care about using the outputted value.

As an example, consider one of the many TryParse methods in .NET such as int.TryParse. The following code show a method that writes to the console whether or not a string can be parsed as an int:

static void ParseInt()
{
    WriteLine("Please enter an int to validate");
    string @int = ReadLine();
    bool isValidInt = int.TryParse(@int, out int parsedInt);
    
    if (isValidInt)
    {
        WriteLine($"{@int} is a valid int");
    }
    else
    {
        WriteLine($"{@int} is NOT a valid int");
    }
}

The preceding method can be written using a discard because the out int parsedInt value is never used:

static void ParseIntUsingDiscard()
{
    WriteLine("Please enter an int to validate");
    string @int = ReadLine();

    if (int.TryParse(@int, out _))
    {
        WriteLine($"{@int} is a valid int");
    }
    else
    {
        WriteLine($"{@int} is NOT a valid int");
    }
}

For example we could create an expression bodied method using a similar approach:

static bool IsInt(string @int) => int.TryParse(@int, out _);

If you have a method that returns a lot of out values such as:

private static void GenerateDefaultCity(out string name, out string nickName, out long population, out DateTime founded)
{
    name = "London";
    nickName = "The Big Smoke";
    population = 8_000_000;
    founded = new DateTime(50, 1, 1);
}

In this case you might only care about the returned population value so you could discard all the other out values:

GenerateDefaultCity(out _,out _, out var population, out _);
WriteLine($"Population is: {population}");

Using C# Discards with Tuples

Another use for discards is where you don’t care about all the fields of a tuple. For example the following method returns a tuple containing a name and age:

static (string name, int age) GenerateDefaultPerson()
{
    return ("Amrit", 42);
}

If you only cared about the age you could write:

var (_, age) = GenerateDefaultPerson();
WriteLine($"Default person age is {age}");

Simplifying Null Checking Code with Discards

Take the following null checking code:

private static void Display(string message)
{
    if (message is null)
    {
        throw new ArgumentNullException(nameof(message));
    }
    WriteLine(message);
}

You could refactor this to make use of throw expressions:

private static void DisplayV2(string message)
{
    string checkedMessage = message ?? throw new ArgumentNullException(nameof(message));

    WriteLine(checkedMessage);
}

In the preceding version however, the checkedMessage variable is somewhat redundant, this could be refactored to use a discard:

private static void DisplayWithDiscardNullCheck(string message)
{
    _ = message ?? throw new ArgumentNullException(nameof(message));
    
    WriteLine(message);
}

Using C# Discards with Tasks

Take the following code:

// Warning CS1998  This async method lacks 'await' operators and will run synchronously.
Task.Run(() => SayHello());

Where the SayHello method is defined as:

private static string SayHello()
{
    string greeting = "Hello there!";
    return greeting;
}

If we don’t care about the return value and want to discard the result and get rid of the compiler warning::

// With discard - no compiler warning
_ = Task.Run(() => SayHello());

If there are any exceptions however, they will be supressed:

await Task.Run(() => throw new Exception()); // Exception thrown
_ = Task.Run(() => throw new Exception()); // Exception suppressed

Pattern Matching with Switch Statements and Discards

You can also use discards in switch statements:

private static void SwitchExample(object o)
{
    switch (o)
    {
        case null:
            WriteLine("o is null");
            break;
        case string s:
            WriteLine($"{s} in uppercase is {s.ToUpperInvariant()}");
            break;
        case var _:
            WriteLine($"{o.GetType()} type not supported.");
            break;
    }
}

If you want to learn a load more C# tips check out my C# Tips and Traps course today. You can even currently start watching with a Pluralsight Free Trial with Unlimited Access .

SHARE:

Simplifying Parameter Null and Other Checks with the GuardClauses Library

Often you want to add null checking and other check code at the start of a method to ensure all the values passed into the method are valid before continuing.

For example the following method checks the name and age:

public static void AddNewPerson(string name, int ageInYears)
{
    if (string.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentException($"Cannot be null, empty, or contain only whitespace.", nameof(name));
    }

    if (ageInYears < 1)
    {
        throw new ArgumentOutOfRangeException(nameof(ageInYears), "Must be greater than zero.");
    }

    // Add to database etc.
}

This “guard” kind of code can “clutter” the method and reduce readability.

One library I recently came across is the Guard Clauses library from Steve Smith.

Once this library is installed we could refactor the preceding code to look like the following:

public static void AddNewPerson(string name, int ageInYears)
{
    Guard.Against.NullOrWhiteSpace(name, nameof(name));
    Guard.Against.NegativeOrZero(ageInYears, nameof(ageInYears));

    // Add to database etc.
}

Passing a null name results in the exception: System.ArgumentNullException: Value cannot be null. (Parameter 'name')

Passing an empty string results in: System.ArgumentException: Required input name was empty. (Parameter 'name')

Passing in an age of zero results in: System.ArgumentException: Required input ageInYears cannot be zero or negative. (Parameter 'ageInYears')

The code is also more readable and succinct.

Out of the box the library comes with the following guards (taken from the documentation):

  • Guard.Against.Null (throws if input is null)
  • Guard.Against.NullOrEmpty (throws if string or array input is null or empty)
  • Guard.Against.NullOrWhiteSpace (throws if string input is null, empty or whitespace)
  • Guard.Against.OutOfRange (throws if integer/DateTime/enum input is outside a provided range)
  • Guard.Against.OutOfSQLDateRange (throws if DateTime input is outside the valid range of SQL Server DateTime values)
  • Guard.Against.Zero (throws if number input is zero)

You can also define your own reusable clauses:

// Define in this namespace so can use alongside built-in guards with no additional namespaces required
namespace Ardalis.GuardClauses
{
    public static class PositiveGuard
    {
        public static void Positive(this IGuardClause guardClause, int input, string parameterName)
        {
            if (input >= 0)
            {
                throw new ArgumentOutOfRangeException(parameterName, $"Required input {parameterName} cannot be positive.");
            }                           
        }
    }
}

And then in a method we can write:

public static void ReportNegativeTemperature(int temp)
{
    Guard.Against.Positive(temp, nameof(temp));
    // Do something
}

And if we pass a positive (or zero) temp we get: System.ArgumentOutOfRangeException: Required input temp cannot be positive. (Parameter 'temp')

This is one of those simple libraries that can make basic tasks easier/more readable.

If you check this out and use it make sure you say thanks to Steve on Twitter and let him know @robertsjason sent you ;)

SHARE:

Writing Azure Functions with Function Monkey: Using Commands Without Handlers

If you’ve read the previous articles on Function Monkey you may be wondering if you always need a command handler. Sometimes you may want to accept a request into the system (for example via HTTP) and that pass that request off for further processing. For example the HTTP data can be accepted and then the data (“command”) can be put on a queue for processing. This allows the potential scale-out of the function that processes queue messages to improve the overall throughput of the system.

Take the following example that allows an invoice to be submitted via HTTP. The submitted invoice is validated before simply being returned from the SubmitInvoiceCommandHandler. The output of the handler gets sent to a storage queue called “invoices”. Then we have a queue storage trigger creating and handling the ProcessInvoiceCommand.

using System.Net.Http;
using System.Threading.Tasks;
using AzureFromTheTrenches.Commanding.Abstractions;
using FluentValidation;
using FunctionMonkey.Abstractions;
using FunctionMonkey.Abstractions.Builders;
using FunctionMonkey.FluentValidation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace FunctionApp2
{
    public class SubmitInvoiceCommand : ICommand<SubmitInvoiceCommand>
    {
        public string Description { get; set; }
        public decimal Amount { get; set; }
    }

    public class SubmitInvoiceCommandValidator : AbstractValidator<SubmitInvoiceCommand>
    {
        public SubmitInvoiceCommandValidator()
        {
            RuleFor(x => x.Description).NotEmpty();
            RuleFor(x => x.Amount).GreaterThan(0);
        }
    }

    public class SubmitInvoiceCommandHandler : ICommandHandler<SubmitInvoiceCommand, SubmitInvoiceCommand>
    {
        public Task<SubmitInvoiceCommand> ExecuteAsync(SubmitInvoiceCommand command, SubmitInvoiceCommand previousResult)
        {
            // We are not actually "handling" anything here, the handler is just returning the sam command
            return Task.FromResult(command);
        }
    }

    public class ProcessInvoiceCommand : ICommand
    {
        public string Description { get; set; }
        public decimal Amount { get; set; }
    }

    public class ProcessInvoiceCommandHandler : ICommandHandler<ProcessInvoiceCommand>
    {
        private readonly ILogger Log;

        public ProcessInvoiceCommandHandler(ILogger log)
        {
            Log = log;
        }

        public Task ExecuteAsync(ProcessInvoiceCommand command)
        {
            Log.LogInformation($"Processing invoice {command.Description} {command.Amount}");
            return Task.CompletedTask;
        }
    }

    public class FunctionAppConfiguration : IFunctionAppConfiguration
    {
        public void Build(IFunctionHostBuilder builder)
        {
            builder
                .Setup((serviceCollection, commandRegistry) =>
                {
                    serviceCollection.AddTransient<IValidator<SubmitInvoiceCommand>, SubmitInvoiceCommandValidator>();
                    commandRegistry.Register<SubmitInvoiceCommandHandler>();
                    commandRegistry.Register<ProcessInvoiceCommandHandler>();
                })
                .AddFluentValidation()
                .Functions(functions => functions

                    .HttpRoute("v1/SubmitInvoice", route => route
                        .HttpFunction<SubmitInvoiceCommand>(HttpMethod.Post)
                        .OutputTo.StorageQueue("invoices"))

                    .Storage(storage => storage
                        .QueueFunction<ProcessInvoiceCommand>("invoices"))                    
                );
        }
    }
}

If we POST the JSON { "Description" : "NAS",    "Amount" : 1000 } we get the following (abridged) output:

Executing HTTP request: {"method": "POST",  "uri": "/api/v1/SubmitInvoice"}
Executing 'SubmitInvoice' 
Executed 'SubmitInvoice'
Executing 'StqFnProcessInvoice' (Reason='New queue message detected on 'invoices')
Storage queue trigger function StqFnProcessInvoice processed a request.
Processing invoice NAS 1000.0
Executed 'StqFnProcessInvoice' 

At the moment the SubmitInvoiceCommandHandler is not doing anything useful, it’s just passing the command back out so it can be output to queue storage.

With Function Monkey you can do away with the command handler in these cases.

One way to do this is when configuring the function app by adding the NoCommandHandler() option in the build method. This means that the SubmitInvoiceCommandHandler class can be deleted:

using System.Net.Http;
using System.Threading.Tasks;
using AzureFromTheTrenches.Commanding.Abstractions;
using FluentValidation;
using FunctionMonkey.Abstractions;
using FunctionMonkey.Abstractions.Builders;
using FunctionMonkey.FluentValidation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace FunctionApp2
{
    public class SubmitInvoiceCommand : ICommand<SubmitInvoiceCommand>
    {
        public string Description { get; set; }
        public decimal Amount { get; set; }
    }

    public class SubmitInvoiceCommandValidator : AbstractValidator<SubmitInvoiceCommand>
    {
        public SubmitInvoiceCommandValidator()
        {
            RuleFor(x => x.Description).NotEmpty();
            RuleFor(x => x.Amount).GreaterThan(0);
        }
    }

    public class ProcessInvoiceCommand : ICommand
    {
        public string Description { get; set; }
        public decimal Amount { get; set; }
    }

    public class ProcessInvoiceCommandHandler : ICommandHandler<ProcessInvoiceCommand>
    {
        private readonly ILogger Log;

        public ProcessInvoiceCommandHandler(ILogger log)
        {
            Log = log;
        }

        public Task ExecuteAsync(ProcessInvoiceCommand command)
        {
            Log.LogInformation($"Processing invoice {command.Description} {command.Amount}");
            return Task.CompletedTask;
        }
    }

    public class FunctionAppConfiguration : IFunctionAppConfiguration
    {
        public void Build(IFunctionHostBuilder builder)
        {
            builder
                .Setup((serviceCollection, commandRegistry) =>
                {
                    serviceCollection.AddTransient<IValidator<SubmitInvoiceCommand>, SubmitInvoiceCommandValidator>();
                    commandRegistry.Register<ProcessInvoiceCommandHandler>();
                })
                .AddFluentValidation()
                .Functions(functions => functions

                    .HttpRoute("v1/SubmitInvoice", route => route
                        .HttpFunction<SubmitInvoiceCommand>(HttpMethod.Post)
                        .Options(options => options.NoCommandHandler())
                        .OutputTo.StorageQueue("invoices"))

                    .Storage(storage => storage
                        .QueueFunction<ProcessInvoiceCommand>("invoices"))                    
                );
        }
    }
}

If we submit the same JSON request we get:

Executing HTTP request: {  "method": "POST",  "uri": "/api/v1/SubmitInvoice"}
Executing 'SubmitInvoice' 
Executed 'SubmitInvoice' 
Executing 'StqFnProcessInvoice' 
Storage queue trigger function StqFnProcessInvoice processed a request.
Processing invoice NAS 1000.0
Executed 'StqFnProcessInvoice'

Even though we don’t have an explicit handler now for the SubmitInvoiceCommand, the validation still takes place.

Another option is to implement the marker interface ICommandWithNoHandler in the command and then you don’t need the .NoCommandHandler() option.

Other Function Monkey articles:

SHARE:

Writing Azure Functions with Function Monkey: Validation

Function Monkey is a framework to define Azure Functions in a fluent way as opposed to using binding attributes on function methods.

Other Function Monkey articles:

In addition to offering a different way to define functions, Function Monkey offers features such as validation.

Consider the following setup that generates a greeting:

using System.Net.Http;
using System.Threading.Tasks;
using AzureFromTheTrenches.Commanding.Abstractions;
using FunctionMonkey.Abstractions;
using FunctionMonkey.Abstractions.Builders;

namespace FunctionApp2
{
    public class GenerateGreetingCommand : ICommand<string>
    {
        public string Name { get; set; }
    }

    public class GenerateGreetingHandler : ICommandHandler<GenerateGreetingCommand, string>
    {
        public Task<string> ExecuteAsync(GenerateGreetingCommand command, string previousResult) => Task.FromResult($"Hello {command.Name}");
    }

    public class FunctionAppConfiguration : IFunctionAppConfiguration
    {
        public void Build(IFunctionHostBuilder builder)
        {
            builder
                .Setup((serviceCollection, commandRegistry) =>
                {
                    commandRegistry.Register<GenerateGreetingHandler>();                    
                })
                .Functions(functions => functions
                    .HttpRoute("v1/GenerateGreeting", route => route
                        .HttpFunction<GenerateGreetingCommand>(HttpMethod.Get))
                );
        }
    }  
}

If we run this and send a JSON payload of {"Name": ""} we’ll get back a response of "Hello ".

There is currently no validation on the name in the GenerateGreetingCommand.

To add validation with Function Monkey install the additional package: FunctionMonkey.FluentValidation. This will also install the dependent package FluentValidation.

To add validation to the Name property, we create a new class that inherits from AbstractValidator<T> where T is the command we want to validate, in this case the GenerateGreetingCommand:

public class GenerateGreetingCommandValidator : AbstractValidator<GenerateGreetingCommand>
{
    public GenerateGreetingCommandValidator()
    {
        RuleFor(x => x.Name).NotEmpty();
    }
}

In the constructor we use the FluentValidation syntax to define what validation to perform on the Name property of the command. In the preceding code we are saying name cannot be empty.

Next we need to wire up this new validator by adding the call to AddFluentValidation() and also registering the validator with serviceCollection.AddTransient<IValidator<GenerateGreetingCommand>, GenerateGreetingCommandValidator>();

So the setup now looks like:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                serviceCollection.AddTransient<IValidator<GenerateGreetingCommand>, GenerateGreetingCommandValidator>();
                commandRegistry.Register<GenerateGreetingHandler>();                    
            })
            .AddFluentValidation()
            .Functions(functions => functions
                .HttpRoute("v1/GenerateGreeting", route => route
                    .HttpFunction<GenerateGreetingCommand>(HttpMethod.Get))
            );
    }
}

If we run the app again and try and submit an empty name, this time we get the following response:

{
  "errors": [
    {
      "severity": 0,
      "errorCode": "NotEmptyValidator",
      "property": "Name",
      "message": "'Name' must not be empty."
    }
  ],
  "isValid": false
}

If we wanted to enforce minimum and maximum Name length:

public class GenerateGreetingCommandValidator : AbstractValidator<GenerateGreetingCommand>
{
    public GenerateGreetingCommandValidator()
    {
        RuleFor(x => x.Name).NotEmpty()
                            .MinimumLength(5)
                            .MaximumLength(10);
    }
}

Now if we try and submit a name of “Joe”:

{
  "errors": [
    {
      "severity": 0,
      "errorCode": "MinimumLengthValidator",
      "property": "Name",
      "message": "The length of 'Name' must be at least 5 characters. You entered 3 characters."
    }
  ],
  "isValid": false
}

To add some custom validation in the form of an Action:

public class GenerateGreetingCommandValidator : AbstractValidator<GenerateGreetingCommand>
{
    public GenerateGreetingCommandValidator()
    {
        RuleFor(x => x.Name).NotEmpty()
                            .MinimumLength(5)
                            .MaximumLength(10)
                            .Custom((name, context) =>
                                {
                                    if (name == "Jason")
                                    {
                                        context.AddFailure("Jason is not a valid name");
                                    }
                                });

    }
}

Submitting a name of “Jason” now results in:

{
  "errors": [
    {
      "severity": 0,
      "errorCode": null,
      "property": "Name",
      "message": "Jason is not a valid name"
    }
  ],
  "isValid": false
}

We could also go and write units tests for the validator.

The ability to define command validation could also be useful if you had multiple ways for a client to submit requests, for example the same command (and validation) could be triggered from HTTP and a queue for example. In this case you could ensure the same validation is executed regardless of the input “channel”.

SHARE:

Writing Azure Functions with Function Monkey: Dependency Injection

In my continued exploration/experimentation with Function Monkey I thought I’d look at how easy/hard it is to inject dependencies into handlers.

Previous articles: Creating Azure Functions with Function Monkey–First Look and Refactoring an Azure Functions App to use Function Monkey.

If you’ve read the previous articles you’ll know the Function Monkey uses the concept of a command to represent “something that needs doing” and a command handler to “do the thing that needs doing”.

An Azure Function trigger results in the creation of a command, that command is passed to a handler, and the handler can return a result to the caller or an output binding.

Good practice dictates good separation of concerns, etc. so you may want to inject dependencies into your handlers to also make them easier to test.

Let’s start off my defining a dependency to represent the generation of a greeting:

public interface IGreetingGenerator
{
    string GenerateGreeting();
}

And we’ll create a basic implementation:

public class TimeOfDayGreetingGenerator : IGreetingGenerator
{
    public string GenerateGreeting()
    {
        var isAfternoon = DateTime.Now.Hour >= 12;

        if (isAfternoon)
        {
            return "Good afternoon";
        }

        return "Good morning";
    }
}

We could now go and write unit tests for this TimeOfDayGreetingGenerator – however we first have to go and provide a way to deterministically provide a specific date and time.

We’ll create another abstraction represent time so the code becomes:

public interface IGreetingGenerator
{
    string GenerateGreeting();
}

public interface ITime
{
    DateTime Now { get; }
}

public class Time : ITime
{
    public DateTime Now => DateTime.Now;
}

public class TimeOfDayGreetingGenerator : IGreetingGenerator
{
    private readonly ITime Time;

    public TimeOfDayGreetingGenerator(ITime time)
    {
        Time = time;
    }

    public string GenerateGreeting()
    {
        var isAfternoon = Time.Now.Hour >= 12;

        if (isAfternoon)
        {
            return "Good afternoon";
        }

        return "Good morning";
    }
}

And some example tests we could write:

public class TimeOfDayGreetingGeneratorShould
{
    [Fact]        
    public void GenerateMorningGreeting()
    {
        var mockTime = new Mock<ITime>();
        mockTime.Setup(x => x.Now).Returns(new DateTime(2020, 1, 1, 11, 59, 59));
        var sut = new TimeOfDayGreetingGenerator(mockTime.Object);

        var greeting = sut.GenerateGreeting();

        Assert.Equal("Good morning", greeting);
    }

    [Fact]
    public void GenerateAfternoonGreeting()
    {
        var mockTime = new Mock<ITime>();
        mockTime.Setup(x => x.Now).Returns(new DateTime(2020, 1, 1, 13, 0, 0));
        var sut = new TimeOfDayGreetingGenerator(mockTime.Object);

        var greeting = sut.GenerateGreeting();

        Assert.Equal("Good afternoon", greeting);
    }
}

The above tests are using the xUnit.net testing framework and Moq: you can learn how to use both of these by following this Pluralsight skills path that features some of my courses. You can start watching with a free trial.

Next we’ll create a command to represent the requirement to create a greeting for a person:

public class GenerateGreetingCommand : ICommand<string>
{
    public string Name { get; set; }
}

We can now create a handler for this command that also takes an IGreetingGenerator as a constructor dependency:

public class GenerateGreetingHandler : ICommandHandler<GenerateGreetingCommand, string>
{
    private readonly IGreetingGenerator GreetingGenerator;

    public GenerateGreetingHandler(IGreetingGenerator greetingGenerator)
    {
        GreetingGenerator = greetingGenerator;
    }
    public Task<string> ExecuteAsync(GenerateGreetingCommand command, string previousResult)
    {
        return Task.FromResult($"{GreetingGenerator.GenerateGreeting()} {command.Name}");
    }
}

And we can add a test:

public class GenerateGreetingHandlerShould
{
    [Fact]
    public async Task GenerateGreetingWithName()
    {
        var mockGenerator = new Mock<IGreetingGenerator>();
        mockGenerator.Setup(x => x.GenerateGreeting()).Returns("mock greeting");
        var sut = new GenerateGreetingHandler(mockGenerator.Object);
        var command = new GenerateGreetingCommand { Name = "Amrit" };

        var greeting = await sut.ExecuteAsync(command, null);

        Assert.Equal("mock greeting Amrit", greeting);
    }
}

Now we have tested some of the moving parts we can put them all together with Function Monkey (note there are more tests cases we should write but we’ll keep this example short):

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                commandRegistry.Register<GenerateGreetingHandler>();
            })
            .Functions(functions => functions
                .HttpRoute("v1/GenerateGreeting", route => route
                    .HttpFunction<GenerateGreetingCommand>(HttpMethod.Get))
            );
    }
}

If we try and run this and submit an HTTP request to the function we’ll get the following error:

Error occurred executing command GenerateGreetingCommand
AzureFromTheTrenches.Commanding: Error occurred during command execution. Microsoft.Extensions.DependencyInjection: Unable to resolve service for type 'FunctionApp2.IGreetingGenerator' while attempting to activate 'FunctionApp2.GenerateGreetingHandler'.

This is because we haven’t wired up the dependencies which we can do by adding:

serviceCollection.AddTransient<ITime, Time>();
serviceCollection.AddTransient<IGreetingGenerator, TimeOfDayGreetingGenerator>();

This makes the entire setup look like the following:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                serviceCollection.AddTransient<ITime, Time>();
                serviceCollection.AddTransient<IGreetingGenerator, TimeOfDayGreetingGenerator>();
                commandRegistry.Register<GenerateGreetingHandler>();                    
            })
            .Functions(functions => functions
                .HttpRoute("v1/GenerateGreeting", route => route
                    .HttpFunction<GenerateGreetingCommand>(HttpMethod.Get))
            );
    }
}

Running the app now and executing the function with the JSON “{"Name": "Sarah"}” returns "Good afternoon Sarah".

It’s nice that DI is built into Function Monkey and that the registration of dependencies is pretty simple.

SHARE:

Refactoring an Azure Functions App to use Function Monkey

In a previous post I took a first look at the Function Monkey library to define Azure Functions using commands and handlers.

In this post I’m going to try and take an existing functions app and convert it to the Function Monkey approach. I should note up-front that this post is not a criticism of the library itself, like everything it’s a work in progress, I may be misunderstanding some of the features :)

The Starting App

The non-function-monkey app creates the workflow: client—>HTTP function –> queue function –> blob function and looks like the following:

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FunctionApp2
{
    public class WordsAdditionRequest
    {
        public IEnumerable<string> Words { get; set; }
    }

    public static class Function1
    {
        [FunctionName("AddWords")]
        public static async Task AddWords(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest request,
            [Queue("WordsToProcess")] IAsyncCollector<string> wordQueue,
            ILogger log)
        {
            // validation/error checking logic omitted for brevity

            log.LogInformation("C# HTTP trigger function processed a request.");

            string requestBody = await new StreamReader(request.Body).ReadToEndAsync();
            WordsAdditionRequest wordsAdditionRequest= JsonConvert.DeserializeObject<WordsAdditionRequest>(requestBody);

            foreach (var word in wordsAdditionRequest.Words)
            {
                log.LogInformation($"Adding word '{word}'");
                await wordQueue.AddAsync(word);
            }
        }

        [FunctionName("ProcessWord")]
        public static void ProcessWord(
            [QueueTrigger("WordsToProcess")] string wordToProcess,
            [Blob("uppercase-words/{rand-guid}")] out string uppercaseWord,
            ILogger log)
        {
            log.LogInformation($"C# Queue trigger for word '{wordToProcess}'");

            uppercaseWord = wordToProcess.ToUpperInvariant();
        }

        [FunctionName("AuditWordWritten")]
        public static void AuditWordWritten(
            [BlobTrigger("uppercase-words/{name}")] string uppercaseWord,
            string name,
            ILogger log)
        {
            log.LogInformation($"C# blob trigger - audit for word '{uppercaseWord}'");
        }
    }
}

Refactoring to Function Monkey

First install the NuGets: FunctionMonkey and FunctionMonkey.Compiler.

The next first step is to create a command, so we’ll change WordsAdditionRequest to:

public class AddWordsCommand: ICommand<int>
{
    public IEnumerable<string> Words { get; set; }
}

So far so good, next we’ll need to create a handler for this command. I need the handler to process the command and return each of the strings so they can be added as separate messages to the queue, so we can fan-out the work:

internal class AddWordsHandler : ICommandHandler<AddWordsCommand, string[]>
{
    public Task<string[]> ExecuteAsync(AddWordsCommand command, string[] previousResult)
    {
        return Task.FromResult(command.Words.ToArray());
    }
}

The next thing is to create the configuration class and wire up the hander as a HTTP-triggered function. The initial attempt looks like this:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                commandRegistry.Register<AddWordsHandler>();
            })
            .Functions(functions => functions
                .HttpRoute("v1/AddWords", route => route
                    .HttpFunction<AddWordsCommand>(HttpMethod.Post)
                    .OutputTo.StorageQueue("WordsToProcess")
                )
            );
    }
}

 

Now we have an HTTP function outputting to a storage queue.

If we run this and submit a HTTP request containing the JSON { "words": ["apple", "pear"]} we get 2 messages added to the WordsToProcess queue.

Messages in the queue

What’s nice here is that because the command and handler return string[] Function Monkey has automatically “fanned-out” the data into multiple messages.When I was writing the code I was looking for specific ways to implement this which wasted some time. Really this is a nice intuitive way of handling IEnumerable return values.

So far so good. The next step is to read the messages from the queue, convert them to upper case, and then write out the blobs. This is unfortunately where I ran into some roadblocks.

First I defined command/hander:

public class ConvertToUpperCaseCommand : ICommand<string>
{
    public string Word { get; set; }
}

internal class ConvertToUpperCaseHandler : ICommandHandler<ConvertToUpperCaseCommand, string>
{        
    public Task<string> ExecuteAsync(ConvertToUpperCaseCommand command, string previousResult)
    {
        return Task.FromResult(command.Word.ToUpperInvariant());
    }
}

Then I tried to modify the config:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                commandRegistry.Register<AddWordsHandler>();
                commandRegistry.Register<ConvertToUpperCaseHandler>();
            })
            .Functions(functions => functions
                .HttpRoute("v1/AddWords", route => route
                    .HttpFunction<AddWordsCommand>(HttpMethod.Post)
                    .OutputTo.StorageQueue("WordsToProcess")                    
                )
            .Storage(storageFunctions => storageFunctions
                .QueueFunction<ConvertToUpperCaseCommand>("WordsToProcess")
                .OutputTo.StorageBlob // StorageBlob does not exist
            );
    }
}

This is where I ran into a problem, I couldn't find an option to output to blob storage:

Visual Studio screenshot showing no method to output to blob storage

There are output bindings for storage queues and tables but not for blobs. I could of course be misunderstanding how to configure this.

To get around this I’m going to try and mix Function Monkey with traditional attributes by adding the following:

[FunctionName("ProcessWord")]
public static void ProcessWord(
    [QueueTrigger("WordsToProcess")] string wordToProcess,
    [Blob("uppercase-words/{rand-guid}")] out string uppercaseWord,
    ILogger log)
{
    log.LogInformation($"C# Queue trigger for word '{wordToProcess}'");

    uppercaseWord = wordToProcess.ToUpperInvariant();
}

Now running the app and making an HTTP request results in the messages being added to the queue via Function Monkey and then the traditionally-specified ProcessWord function executes and writes to blob storage.

It’s nice that you can mix Function Monkey with the attribute-based function definition, though I don’t know if this is recommended or not and whether not there could by any unintentional side-effects.

The final part is the blob-triggered audit function.

Once again I’ll define a command and handler:

public class AuditCommand : ICommand, IStreamCommand
{
    public Stream Stream { get; set; }

    public string Name { get; set; }
}
internal class AuditHandler : ICommandHandler<AuditCommand>
{
    public Task ExecuteAsync(AuditCommand command)
    {           
        using (StreamReader reader = new StreamReader(command.Stream))
        {
            string name = reader.ReadToEnd();

            // How to log name?
        }

        return Task.CompletedTask;
    }
}

The version of a blob-triggered command allows us to get a Stream representing the blob data (simpler blob commands without streams are also available).

We can now wire this up:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
    public void Build(IFunctionHostBuilder builder)
    {
        builder
            .Setup((serviceCollection, commandRegistry) =>
            {
                commandRegistry.Register<AddWordsHandler>();
                commandRegistry.Register<AuditHandler>();
            })
            .Functions(functions => functions
                .HttpRoute("v1/AddWords", route => route
                    .HttpFunction<AddWordsCommand>(HttpMethod.Post)
                    .OutputTo.StorageQueue("WordsToProcess"))
                .Storage(storageFunctions => storageFunctions
                .BlobFunction<AuditCommand>("uppercase-words/{name}"))
            );
    }
}

Running this now results in the blob being written and the audit command handler executing. The original hander just output the blob to the log.

I’m not sure how to get access to the log in a handler, so I’m going to just add a constructor that takes an ILogger and see what happens:

internal class AuditHandler : ICommandHandler<AuditCommand>
{
    private readonly ILogger log;

    public AuditHandler(ILogger log)
    {
        this.log = log;
    }
    public Task ExecuteAsync(AuditCommand command)
    {           
        using (StreamReader reader = new StreamReader(command.Stream))
        {
            string name = reader.ReadToEnd();

            log.LogInformation($"C# blob trigger - audit for word '{name}'");
        }

        return Task.CompletedTask;
    }
}

Running this and it just works, the ILogger is injected into the handler and the log message is output: C# blob trigger - audit for word 'APPLE'

There’s a lot more to Function Monkey by the looks of it such as DI, validation, etc.

The final code:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using AzureFromTheTrenches.Commanding.Abstractions;
using FunctionMonkey.Abstractions;
using FunctionMonkey.Abstractions.Builders;
using FunctionMonkey.Commanding.Abstractions;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace FunctionApp2
{
    public class AddWordsCommand : ICommand<string[]>
    {
        public IEnumerable<string> Words { get; set; }
    }

    internal class AddWordsHandler : ICommandHandler<AddWordsCommand, string[]>
    {
        public Task<string[]> ExecuteAsync(AddWordsCommand command, string[] previousResult)
        {
            return Task.FromResult(command.Words.ToArray());
        }
    }

    public class AuditCommand : ICommand, IStreamCommand
    {
        public Stream Stream { get; set; }

        public string Name { get; set; }
    }

    internal class AuditHandler : ICommandHandler<AuditCommand>
    {
        private readonly ILogger log;

        public AuditHandler(ILogger log)
        {
            this.log = log;
        }
        public Task ExecuteAsync(AuditCommand command)
        {           
            using (StreamReader reader = new StreamReader(command.Stream))
            {
                string name = reader.ReadToEnd();

                log.LogInformation($"C# blob trigger - audit for word '{name}'");
            }

            return Task.CompletedTask;
        }
    }

    public class FunctionAppConfiguration : IFunctionAppConfiguration
    {
        public void Build(IFunctionHostBuilder builder)
        {
            builder
                .Setup((serviceCollection, commandRegistry) =>
                {
                    commandRegistry.Register<AddWordsHandler>();
                    commandRegistry.Register<AuditHandler>();
                })
                .Functions(functions => functions
                    .HttpRoute("v1/AddWords", route => route
                        .HttpFunction<AddWordsCommand>(HttpMethod.Post)
                        .OutputTo.StorageQueue("WordsToProcess"))
                    .Storage(storageFunctions => storageFunctions
                    .BlobFunction<AuditCommand>("uppercase-words/{name}"))
                );
        }
    }

    public static class Function1
    {
        [FunctionName("ProcessWord")]
        public static void ProcessWord(
            [QueueTrigger("WordsToProcess")] string wordToProcess,
            [Blob("uppercase-words/{rand-guid}")] out string uppercaseWord,
            ILogger log)
        {
            log.LogInformation($"C# Queue trigger for word '{wordToProcess}'");

            uppercaseWord = wordToProcess.ToUpperInvariant();
        }
    }   
}

SHARE: