Service Fabric 单元测试和依赖注入

Service Fabric Unit Testing and Dependency Injection

我无法通过仅调用其构造函数然后测试其方法来测试 Reliable Service/Actor。 var testService = new SomeService(); 抛出 NullReferenceException。那么我可以用部署的服务做什么..

我知道部署的 SF Reliable Services/Actors 不是标准的 .NET 类,部署的单元测试 S/A 可能是一个奇怪的想法。

无论如何,我现在正在尝试一下。

例如。我刚刚部署了一个服务,而不是在测试中我创建了一个代理对象并将项目添加到服务的输入队列中。然后我需要断言输入队列计数 = 1。如果我刚刚部署了一个服务并且没有其他客户端/Services/Actors 使用它的输入队列,它就会工作。但是下次这个测试将失败,这就是问题所在。我需要让服务停止与其他消费者合作,放弃它的队列而不是测试它。为此,我可以创建一些 TestMode 属性 和一些方法,例如 PropareoForTests/TestingCompleted,并在测试前后从测试客户端调用它们。

这样做不是个好主意吗?也许有一些单元测试 SF 的指南?谢谢

更新:

在调查 Service Fabric Web Reference Application example 时,我发现了这个 TODO 字符串:

/// TODO: Temporary property-injection for an IServiceProxyWrapper until constructor injection is available.

这是否意味着顺丰服务将改进其 DI 支持?演员呢?

实际上,您可以像在 .NET 中测试任何其他 class 一样测试 Reliable Services 和 Actors!它们的特殊之处在于它们使用特定的钩子连接到底层平台,但除此之外,您可以正常实例化您的服务或参与者 class 并在其上调用方法。

目前,Reliable Services 更容易进行单元测试,因为平台的主要挂钩状态管理器是一个可通过构造函数插入的接口。

例如,您的服务 class 可能如下所示:

编辑:更新为 GA 版本 API (2.0.135)

class MyService : StatefulService
{
  public MyService (StatefulServiceContext context, IReliableStateManager stateManager)
      :base (context, stateManager)
  {
  }

  public void MyMethod()
  {
    // do stuff..
  }
}

然后您可以像这样测试您的服务 class:

[TestMethod]
public TestMyMethod()
{
  MockReliableStateManager stateManager = new MockReliableStateManager();
  MyService target = new MyService(stateManager);

  target.MyMethod();
  // validate results and all that good stuff
}

我们有一个实际服务的完整工作示例,其中有很多依赖项正在 GitHub 上进行单元测试:https://github.com/Azure-Samples/service-fabric-dotnet-management-party-cluster

此示例还有 IReliableStateManager 和 IReliableDictionary 模拟,您可以将它们用作您自己的单元测试的起点。

要在 Reliable Actors 中模拟状态管理器,您可以这样做:

private readonly IActorStateManager _stateManager;

public MyActor(IActorStateManager stateManager = null)
{
    _stateManager = stateManager ?? this.StateManager;
}

实际上,StateManager 此时尚未初始化。我们可以在调用OnActivateAsync的时候得到:

private IActorStateManager _stateManager;

// Unit tests can inject mock here.
public MyActor(IActorStateManager stateManager = null)
{
    _stateManager = stateManager;
}

protected override async Task OnActivateAsync()
{
    if (_stateManager == null)
    {
        _stateManager = StateManager;
    }
}

只需确保在其余代码中始终使用 _stateManager 而不是 this.StateManager