In my last two posts, I’ve been building up the infrastructure needed to write request tests for a C# Azure Functions API:
- Setting Up an Azure Functions Dependency Injection Context for Request Tests
- Testing JSON Input/Output in Azure Functions Request Tests
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!
Great article, did you ever get to implementing the next steps?
Thanks!
And yes, I did get one final post for the series up: https://spin.atomicobject.com/2020/12/09/azure-functions-request-tests/
(I’ve been meaning to go back and add links to the previous posts but haven’t gotten around to it)