如何最好地对使用 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(),或者通过在中注册 IServiceGatewayFactory
或 IServiceGateway
你的国际奥委会,这是默认实现:
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<MyService>(); srv.Get(new MyDto(...))
。 MyService 调用(内部)服务 OtherService。在测试中,OtherService 被 mocked 并添加到 BasicAppHost 的 容器中。在 MyService.Get(MyDto ..)
内部使用 Resolve<OtherService>
可以正常工作,但不推荐。 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 服务进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?