.net-coredependency-injectionazure-functionsxunitfunctional-testing

How to do Functional Testing for Azure Functions?


I'm wondering if anyone has figured out how to perform functional testing on the class that implements Azure Functions.

First, there is this nice article about how to set up testing for the function class that is generated by the Visual Studio (2019) Azure Functions project template. The problem is that my Azure Functions implementation uses dependency injection and cannot use static classes like the ones generated in a new Azure Functions project. My implementation uses a Startup class that is similar to what you would find in this article about Azure Functions with dependency injection.

I've included a simple function method that returns the system version. Here is what the Startup and function classes look like:

[assembly: FunctionsStartup(typeof(SomeNamespace.FunctionApp.Startup))]
namespace SomeNamespace.FunctionApp
{
    public sealed class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSomeService(); // IServiceCollection extension method defined elsewhere.
        }
    }

    public class SomeFunctions
    {
        public SomeFunctions(IConfiguration configuration, ISomeService someService)
        {
            _someService = someService;

            Version currVersion = GetType().Assembly.GetName().Version;
            _systemVersion = new Version(currVersion.Major, currVersion.Minor, configuration.GetValue<Int32>("BuildId")).ToString();
        }

        private readonly ISomeService _someService;
        private readonly String _systemVersion;

        [FunctionName("SystemVersion")]
        public ActionResult<String> SystemVersion([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "SomeFunctions/SystemVersion")] HttpRequest req)
        {
            return new JsonResult(_systemVersion) { StatusCode = (Int32)HttpStatusCode.OK };
        }
    }
}

For reference, this is what the functional test class looks like for a Web API project that implements a controller class. Please note that the Startup class listed below is similar, but different from, the Startup class used for the SomeFunctions class.

namespace SomeNamespace.WebAPI.Controllers
{
    public sealed class TestSomeController : IClassFixture<WebApplicationFactory<Startup>>
    {
        public TestSomeController(WebApplicationFactory<Startup> factory)
        {
            _client = factory.CreateClient();
        }

        private readonly HttpClient _client;

        [Fact]
        public async Task Get()
        {
            var httpResponse = await _client.GetAsync("api/SomeRoute");
            Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode);
        }
    }
}

I'm trying to find a way to do something similar for the SomeFunctions class above. If I do something similar to the implementation of the Web API project above, I get this error message:

System.InvalidOperationException: 'The provided Type 'Startup' does not belong to an assembly with an entry point. A common cause for this error is providing a Type from a class library.'

How can I implement a functional testing class for the Azure Functions in the SomeFunctions implementation above?


Solution

  • I was able to find a great article about how to perform integration testing with Azure Functions. This testing implementation is not quite a full functional test where an HTTP request is sent to a URL, but it is enough to test the complete application stack from the scope of the Azure Function method implementation inward.

    The gist of the test class looks like this:

    namespace SomeNamespace.FunctionApp
    {
        public sealed class TestSomeFunctions
        {
            public TestSomeFunctions()
            {
                var startup = new Startup();
                var host = new HostBuilder()
                    .ConfigureWebJobs(startup.Configure)
                    .Build();
    
                _someFunctions = new SomeFunctions(SomeFunctions);
            }
    
            private readonly SomeFunctions _someFunctions;
    
            [Fact]
            public void SystemVersion()
            {
                var request = new DefaultHttpRequest(new DefaultHttpContext());
                JsonResult jsonResult = _someFunctions.SystemVersion(request).Result as JsonResult;
                Assert.Equal((Int32)HttpStatusCode.OK, jsonResult.StatusCode);
            }
        }
    }