Azure函数使用静态属性测试函数

时间:2019-08-13 12:21:20

标签: azure-functions

我可以使用Xunit这样测试我的Azure Function:

var req = GenerateReq();
var res = await MyFunc.Run(req, logger);

如果在我的函数中,我像这样生成一个CosmosDb DocumentClient:

    static DocumentClient docClient = GetCustomClient();

    private static DocumentClient GetCustomClient()
    {
        string cosmosUrl = string.Empty;
        string cosmosKey = string.Empty;
        cosmosUrl = Environment.GetEnvironmentVariable("cosmosUrl");
        cosmosKey = Environment.GetEnvironmentVariable("cosmosKey");
        DocumentClient customClient = new DocumentClient(new Uri(cosmosUrl), cosmosKey,
            new ConnectionPolicy
            {
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp,
                // Customize retry options for Throttled requests
                RetryOptions = new RetryOptions()
                {
                    MaxRetryAttemptsOnThrottledRequests = 10,
                    MaxRetryWaitTimeInSeconds = 30
                }
            });
        return customClient;
    }

当我尝试访问docClient时,出现异常:

The type initializer for 'MyFunc.Get' threw an exception.

是否有解决此问题的方法?

3 个答案:

答案 0 :(得分:1)

您可以通过使用启动类并设置依赖项注入来解决此问题。然后,可以生成其使用的IDocumentClient接口,而不是生成DocumentClient。从那里,您可以在测试期间模拟IDocumentClient。

我写过有关设置启动类here

的文章

答案 1 :(得分:1)

这可能是由于错误的静态构造函数或错误的静态属性/字段的内联初始化引起的。例如:

class MyFunc
{
    static MyFunc()
    {
        //buggy code here
    }
    static DocumentClient docClient = Buggy_GetCustomClient(); // <-- or here
}

以上将在第一次使用TypeInitializationException之前引起MyFunc

运行该代码时是否定义了环境变量cosmosUrlcosmosKey如果不是,则可能是原因。 GetCustomClient最终将引发异常,并将其包装在TypeInitializationException中。

如Napoloeon的回答中所建议,我建议使用依赖注入来获取IDocumentClient的实例(假设您正在使用函数v2)。看一下这个answer,它显示了如何在Azure Functions v2中注入和使用IDocumentClient。 Azure函数的DI支持的正式文档为here

答案 2 :(得分:1)

您肯定应该使用依赖注入来解决此问题。当前,您将在单元测试中使用实际的DocumentClient,这是一个不好的做法-您应该使用DocumentClient mock ,以便您控制行为。 / p>

here所述,创建一个Startup.cs文件并注册您的客户端:

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(MyApp.Startup))]
namespace MyApp
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            IDocumentClient client = GetCustomClient();
            builder.Services.AddSingleton<IDocumentClient>(client);
        }

        private static DocumentClient GetCustomClient()
        {
            string cosmosUrl = Environment.GetEnvironmentVariable("cosmosUrl");
            string cosmosKey = Environment.GetEnvironmentVariable("cosmosKey");

            DocumentClient customClient = new DocumentClient(new Uri(cosmosUrl), cosmosKey, new ConnectionPolicy
            {
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp,
                // Customize retry options for Throttled requests
                RetryOptions = new RetryOptions()
                {
                    MaxRetryAttemptsOnThrottledRequests = 10,
                    MaxRetryWaitTimeInSeconds = 30
                }
            });
            return customClient;
        }
    }
}

然后,您可以在函数中插入IDocumentClient作为输入绑定:

[CosmosDB("DatabaseName", "CollectionName")] IDocumentClient documentClient

在单元测试中,您可以使用Moq之类的库来模拟IDocumentClient

private Mock<IDocumentClient> _mockDocumentClient = new Mock<IDocumentClient>();    

var req = GenerateReq();
var res = await MyFunc.Run(req, logger, _mockDocumentClient.Object);