如何最好地对使用 IServiceGateway 调用其他内部服务的 ServiceStack 服务进行单元测试

Posted

技术标签:

【中文标题】如何最好地对使用 IServiceGateway 调用其他内部服务的 ServiceStack 服务进行单元测试【英文标题】:How best to unit test a ServiceStack service that uses IServiceGateway to call other internal services 【发布时间】:2020-08-15 09:11:02 【问题描述】:

我一直在遵循这里的指南 - https://docs.servicestack.net/testing

我正在尝试进行单元测试而不是集成,只是为了降低模拟级别和其他复杂性。

我的一些服务通过推荐的 IServiceGateway API 调用我的其他一些服务,例如Gateway.Send(MyRequest)。

但是在运行测试时我得到 System.NotImplementedException: 'Unable to resolve service 'GetMyContentRequest''。

我使用了处理这个请求的服务 container.RegisterAutoWired()。

我不确定下一步该去哪里。我真的不想重新开始设置集成测试模式。

【问题讨论】:

你最终是如何解决这个问题的?我也有同样的挑战。如果我不使用网关,它可以正常工作,只需从调用方解析“其他服务”,但这似乎不是推荐的方式。 【参考方案1】:

如果您尝试将服务集成作为单元测试执行,而不是在经过验证的有效状态下启动的集成测试,您可能会不断遇到问题。

但是对于网关请求,它们是使用 IServiceGateway 执行的,您可以选择在自定义 AppHost 中使用自定义实现覆盖 implementing GetServiceGateway(),或者通过在中注册 IServiceGatewayFactoryIServiceGateway你的国际奥委会,这是默认实现:

public virtual IServiceGateway GetServiceGateway(IRequest req)

    if (req == null)
        throw new ArgumentNullException(nameof(req));

    var factory = Container.TryResolve<IServiceGatewayFactory>();
    return factory != null ? factory.GetServiceGateway(req) 
        : Container.TryResolve<IServiceGateway>()
        ?? new InProcessServiceGateway(req);

【讨论】:

是的,我刚刚意识到这可能会导致电话持续呈螺旋式上升。 没有更简单的方法吗?基于 AppHost 的集成测试很慢,所以我更喜欢基于 BasicAppHost 的“单元测试”。内部测试我会做var srv = container.Resolve&lt;MyService&gt;(); srv.Get(new MyDto(...))MyService 调用(内部)服务 OtherService。在测试中,OtherService 被 mocked 并添加到 BasicAppHost 的 容器中。在 MyService.Get(MyDto ..) 内部使用 Resolve&lt;OtherService&gt; 可以正常工作,但不推荐。 SS docs 推荐Gateway.Send,这不起作用(找不到服务)。 @specimen 如果您只是想测试您的服务,您可以从 IOC 解决它并直接调用该方法,但这不会测试任何 AppHost 集成,如果您的服务尝试将失败访问 IRequest 或 IResponse 否则应该没问题。但是我总是推荐集成测试,这是对真实环境的更好测试。如果速度较慢,则测试夹具较少但较大,因此每个夹具只需执行一次 AppHost 启动成本。 @specimen 如果你的服务使用服务网关,你需要在 IOC 中使用集成测试或模拟 IServiceGateway 感谢您的耐心等待。我做错的是模拟服务,而我应该模拟网关。我已经花时间发布完整的答案,为后代【参考方案2】:

根据@mythz 的回答中的讨论,这是我的解决方案:

像OP这样的用例:测试“主”服务,并模拟“子服务”,就像OP一样,我想用单元测试(所以BasicAppHost)来做到这一点,因为它更快,我相信它是以这种方式更容易模拟服务(旁注:对于基于 AppHost 的集成测试,SS 将扫描程序集以查找(真实)服务,那么如何模拟?从“容器”中注销并替换 w.mock?)。

无论如何,对于单元测试:

我的“主要”服务正在使用另一个服务,通过 IServiceGateway(这是官方推荐的方式):

public MainDtoResponse Get(MainDto request) 
  // do some stuff
  var subResponse = Gateway.Send(new SubDto  /* params */ );
  // do some stuff with subResponse

在我的测试设置中:

appHost = new BasicAppHost().Init();

var mockGateway = new Mock<IServiceGateway>(); // using Moq
mockGateway.Setup(x => x.Send<SubDtoResponse>(It.IsAny<SubDto>()))
  .Returns(new SubDtoResponse  /* ... */ );
container.Register(mockGateway.Object);

所以必须mock IServiceGateway,然后Send方法才是重要的。我做错的是模拟服务,而我应该模拟网关。

然后以正常方式调用 主服务(正在测试中)进行单元测试,就像在文档中一样:

var s = appHost.Container.Resolve<MainService>(); // must be populated in DI manually earlier in code
s.Get(new MainDto  /* ... */ )

PS:mockGateway.Setup 可以在每个测试中使用,不一定在OneTimeSetUp中。

【讨论】:

以上是关于如何最好地对使用 IServiceGateway 调用其他内部服务的 ServiceStack 服务进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

LLVM IR:有效地对向量求和

如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?

Flutter:如何正确地对“列表”进行 JSON 编码?

如何允许 scss 开玩笑地对 typescript nextjs 进行单元测试?

如何在图像时间序列中一致地对轮廓进行编号?

如何在 Android 上更好地对 Looper 和 Handler 代码进行单元测试?