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:

Comments (2) -

  • Mike Black

    2/11/2020 1:25:27 PM | Reply

    Thanks for these articles.  My biggest concern is how feasible it is to keep up with the pace of changes to Azure Functions.  I like the use of Mediatr in Function Monkey and the code is pretty streamlined.  Your previous article plus others I've read discuss how to work around some of the limitations but it still makes me nervous.  I'm getting ready to write Azure functions to handle events and interactive components for a Slack app and  this is tempting to use.

    I like your blog so keep up the good work.  I'll check out your Pluralsight classes as well.

    • Jason Roberts

      2/12/2020 12:19:19 AM | Reply

      Hi Mike, like with any external dependency you take, you introduce additional risks to the project you are working on. I do like the Function Monkey approach and I think you are right to cautious introducing any new library. I would also say if you are new to Azure Functions and the concepts such as triggers, binding etc. then you should probably start with the non-Function-Monkey approach to learn the concepts first (ideally on a small self-contained project/service).

Pingbacks and trackbacks (2)+

Add comment

Loading