改装 TestServer 和 NSubstitute

Refit with TestServer and NSubstitute

我正在尝试使用 Refit 在 TestServer 上测试 Rest 接口,同时将 DateTime.Now 替换为 return 自定义时间。

我的改装界面是这样的:

public interface IMyApi
    {
        DateTime CurentTime();
        [Get("/api/...")]
        Task<ApiResponse<DateTime>> SomeOtherFunction();
    }

在我的实现中,我有

public class MyApi {
  public virtual DateTime CurrentTime { return DateTime.Now; } 
  ...
  public async Task<IActionResult> SomeOtherFunction() { return CurrentTime();  }
}

这作为单元测试效果很好

var myMock = Substitute.ForPartsOf<MyApi>();
myMock.Configure().CurrentTime().Returns(..some fixed date..);
myMock.SomeOtherFunction();
// returns the fixed date

但是,当我在 TestServer 上使用 Refit 和 运行 创建双胞胎时,我不知道如何让它工作,因为原始函数不断被调用:

var myMock = Substitute.ForPartsOf<IMyApi>();
myMock.Configure().CurrentTime().Returns(..some fixed date..);
var myServer = new TestServer(builder: ... );

// how do I connect to the invokation below?
var client = RestService.For<IMyApi>(myServer.CreateClient());
client.SomeOtherFunction();

所以问题是,如何从模拟的 API 而不是原始的 api 创建 RestService.For<>?或者您还有其他建议吗?

想法是我们需要替换控制器工厂在 运行 时间创建的控制器。我发现最简单的方法是:

  1. 在 Startup class 的“ConfigureServices”中,告诉框架将控制器存储在服务集合中。我的现在看起来有点像这样:
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddControllers().AddControllersAsServices()
                  .AddOtherThings(...)                                                       );
            ...
         }
  1. 将控制器替换为模拟控制器,在某个地方可以访问控制器集合(例如在 WebHostBuilder 的 ConfigureTestServices() 中):
    var toRemove = svc.FirstOrDefault(d => d.ServiceType == typeof(MyApi));
    svc.Remove(toRemove);


    var c = Substitute.ForPartsOf<MyApi>(new object[] { ...constructor params... });
    svc.AddScoped(controller => c);
    controller.Configure().CurrentTime().Returns(DateTime.Parse("1/1/2001 10:00:00"));

这里的游戏有点晚了,但是你试过使用 RefitSettings 吗?下面是 MockHttpMessageHandler 的示例。我之前用它来模拟 Refit 注入。

using var mockHttp = new MockHttpMessageHandler(); 
var settings = new RefitSettings { HttpMessageHandlerFactory = () => mockHttp };            
var apiServiceMock = RestService.For<IRealTimePayment>("https://api.github.com", settings);