改装 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<>?或者您还有其他建议吗?
想法是我们需要替换控制器工厂在 运行 时间创建的控制器。我发现最简单的方法是:
- 在 Startup class 的“ConfigureServices”中,告诉框架将控制器存储在服务集合中。我的现在看起来有点像这样:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers().AddControllersAsServices()
.AddOtherThings(...) );
...
}
- 将控制器替换为模拟控制器,在某个地方可以访问控制器集合(例如在 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);
我正在尝试使用 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<>?或者您还有其他建议吗?
想法是我们需要替换控制器工厂在 运行 时间创建的控制器。我发现最简单的方法是:
- 在 Startup class 的“ConfigureServices”中,告诉框架将控制器存储在服务集合中。我的现在看起来有点像这样:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers().AddControllersAsServices()
.AddOtherThings(...) );
...
}
- 将控制器替换为模拟控制器,在某个地方可以访问控制器集合(例如在 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);