【问题标题】:Azure Functions Testing Functions With Static PropertiesAzure Functions 使用静态属性测试函数
【发布时间】:2019-08-15 16:05:35
【问题描述】:

我可以像这样使用 Xunit 测试我的 Azure 函数:

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.

有没有办法解决这个问题?

【问题讨论】:

    标签: azure-functions


    【解决方案1】:

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

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

    在第一次使用MyFunc 之前,上面会导致TypeInitializationException

    您是否在代码运行时定义了环境变量 cosmosUrlcosmosKey 如果没有,这很可能是原因。 GetCustomClient 最终会抛出一个异常,并被包裹在 TypeInitializationException 中。

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

    【讨论】:

    • 我应该能够在 GetCustomClient 上设置断点吗?当 documentClient 被调用时,它立即抛出异常并且代码似乎没有运行..
    • 无法在静态初始化的字段或静态 ctor 中设置断点。一种方法是不进行静态初始化。所以改成static DocumentClient docClient; 然后在MyFunc.Run() 方法调用中:docClient = GetCustomClient(); 这应该可以让断点被命中。
    • 最初是环境变量导致了问题。我在 Azure 中也有它们,但在 local.settings.json 中也有,但是 XUnit 没有拾取 local.settings.json 文件,这真的很烦人?
    • 这个 SO Answer 给出了保持键关闭 Git Hub 和设置环境变量的答案。感谢您的帮助 grrreat 回答! stackoverflow.com/questions/48307861/…
    • 我建议不要像你建议的那样盲目地做 DI
    【解决方案2】:

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

    我写过设置启动类here

    【讨论】:

      【解决方案3】:

      您绝对应该使用依赖注入来解决这个问题。目前,您将在单元测试中使用实际的DocumentClient,这是一种不好的做法——您应该使用DocumentClientmock,这样您就可以控制它的行为。

      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);
      

      【讨论】:

        猜你喜欢
        • 2013-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-10
        • 1970-01-01
        • 2015-11-12
        • 2018-09-28
        • 1970-01-01
        相关资源
        最近更新 更多