服务结构单元测试和依赖注入

本文关键字:依赖 注入 单元测试 结构 服务 | 更新日期: 2023-09-27 18:21:58

我不能通过调用可靠服务/参与者的构造函数然后测试它的方法来测试它。var testService = new SomeService();抛出一个NullReferenceException。那么,我可以对部署的服务做些什么。。

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

不管怎样,现在我正试着试一试。

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

这样做是个坏主意吗。也许有一些单元测试SF的指导方针吗?谢谢

更新:

在研究Service Fabric Web参考应用程序示例时,我发现了以下TODO字符串:

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

这是否意味着SF Services将改进其DI支持?演员呢?

服务结构单元测试和依赖注入

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

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

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

编辑:更新为API正式发行版(2.0.135)

class MyService : StatefulService
{
  public MyService (StatefulServiceContext context, IReliableStateManager stateManager)
      :base (context, stateManager)
  {
  }
  public void MyMethod()
  {
    // do stuff..
  }
}

然后你可以这样测试你的服务类:

[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