Using Reflection to Find Azure Function Methods

In my last two posts, I’ve been building up the infrastructure needed to write request tests for a C# Azure Functions API:

My goal is to write tests that are as close to end-to-end as I can get, while still staying in the familiar confines of the xUnit test suite. I want the productivity that comes from having the tests written in the same language, running in the same IDE, and using all of the familiar tooling.

In this post, I’m going to collect all of the “HttpTrigger” methods that make up the API.

Reflection

Using reflection, it’s possible to iterate through all of the methods in a class, including inspecting custom attributes of the method and the method’s arguments.

We’ll use the following as an example of an HTTP-triggered function:


public class UserFunction
{
  [FunctionName("GetUser")]
  public async Task<IActionResult> GetUser(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "users/{id}")]
    string id,
    ILogger log)
{
  log.LogInformation($"Getting user {id}...");
  return new OkObjectResult(new
  {
    id,
    username = "Testing"
  });
}

To start, you need the Type of the class:


var classType = typeof(UserFunction);

Locate the HTTP-Triggered Functions

Once you have the Type of a class, it’s possible to find all of the methods that are public, have the FunctionName custom attribute, and have an an argument with the HttpTrigger custom attribute:


var functionMethods = classType.GetMethods().Where(info =>
  info.IsPublic &&
  info.GetCustomAttributes().Any(attr => attr is FunctionNameAttribute) &&
  info.GetParameters().Any(
    paramInfo => paramInfo.GetCustomAttributes().Any(attr => attr is HttpTriggerAttribute)));

Extract HTTP Trigger Data

We now have a list of all of the methods (MethodInfo instances) on the class that are being exposed in the API. We need to know which method to invoke given an HttpRequest (which is my end goal for how I’ll invoke these methods in a request test). That means we need to extract the route and the HTTP verbs (GET, POST, etc.) that each method is configured to handle.

We’ll use a simple class to hold the details that will be extracted from the custom attributes:


public class AzureFunctionInfo
{
    public string Route { get; set; }
    public string[] HttpMethods { get; set; }
    public MethodInfo MethodInfo { get; set; }
}

The AzureFunctionInfo for each method can now be extracted:


var azureFunctions = functionMethods.Select(info =>
{
    var customAttribute = (HttpTriggerAttribute)info
        .GetParameters()
        .SelectMany(paramInfo => paramInfo.GetCustomAttributes())
        .First(attr => attr is HttpTriggerAttribute);

    return new AzureFunctionInfo
    {
      Route = customAttribute.Route,
      HttpMethods = customAttribute.Methods,
      MethodInfo = info,
    };
});

For the example GetUser function, the Route would be "users/{id}" and the HttpMethods would be ["get"].

What’s Next?

Now that I’ve collected all of the HTTP-triggered functions in the API, I want to be able to invoke the correct one given only an HttpRequest. That’s going to require matching routes and figuring out the correct arguments to pass to the method. I’ll be working on that in the next post!