使用 asp.net 5 TestServer 模拟外部 Api 调用
Using asp.net 5 TestServer to Mock an external Api call
我正在尝试使用 TestServer 来测试我的中间件。
在中间件的某处,我的代码正在通过 HttpClient 调用 api。我想通过使用第二个 TestServer 来模拟这个,但想知道这是否可能。我已经尝试过,但出现错误:"Trying to connect ... but the server actively refused it."
代码如下所示:
[Fact]
public async Task Should_give_200_Response()
{
var server = TestServer.Create((app) =>
{
app.UseMiddleware<DummyMiddleware>();
});
var fakeServer = TestServer.Create((app) =>
{
app.UseMiddleware<FakeMiddleware>();
});
using(server)
{
using(fakeServer)
{
var response = await server.CreateClient().GetAsync("/somePath");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
在 DummyMiddleware 代码的某处,我正在做一个
HttpClient client = new HttpClient() { BaseAddress = "http://fakeUrl.com"};
var resp = client.GetAsync("/path/to/api");
url : http://fakeUrl.com would be mocked by the fakeServer. but in fact a real call to http://fakeUrl 是通过网络发布的,但我希望它能命中假服务器,这样我就可以模拟 api.
假设假服务器会模拟 google 日历 api 例如。
更新 : 使用fakeServer
事实上,fakeServer 会听这个 url :“http://fakeUrl.com" and when receive the route "/path/to/api" it would return a json object for instance. What I would like to, is my fakeServer to be returned a mocked object. as a reminder, "http://fakeUrl.com/path/to/api”会在我的代码中的某个地方用 HttpClient 对象调用。
我找到了一个解决方案,但是 visual studio 2015 CTP6 测试有时会通过,有时不会...这似乎是 Xunit 问题,因为当我调试时,一切都很好...但事实并非如此问题在这里
link 到完整代码:github repo
这是我要测试的中间件:
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Multi.Web.Api
{
public class MultiMiddleware
{
private readonly RequestDelegate next;
public MultiMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, IClientProvider provider)
{
HttpClient calendarClient = null;
HttpClient CalcClient = null;
try
{
//
//get the respective client
//
calendarClient = provider.GetClientFor("calendar");
CalcClient = provider.GetClientFor("calc");
//
//call the calendar api
//
var calendarResponse = "";
if (context.Request.Path.Value == "/today")
{
calendarResponse = await calendarClient.GetStringAsync("http://www.calendarApi.io/today");
}
else if (context.Request.Path.Value == "/yesterday")
{
calendarResponse = await calendarClient.GetStringAsync("http://www.calendarApi.io/yesterday");
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
//does not process further
return;
}
//
//call another api
//
var calcResponse = await CalcClient.GetStringAsync("http://www.calcApi.io/count");
//
// write the final response
//
await context.Response.WriteAsync(calendarResponse + " count is " + calcResponse);
await next(context);
}
finally
{
if (calendarClient != null)
{
calendarClient.Dispose();
}
if (CalcClient != null)
{
CalcClient.Dispose();
}
}
}
}
public static class MultiMiddlewareExtensions
{
public static IApplicationBuilder UseMulti(this IApplicationBuilder app)
{
return app.UseMiddleware<MultiMiddleware>();
}
}
}
注意 Invoke 方法正在获取一个 IClientProvider(通过 DI),它将 return 基于一些字符串的不同 HttpClient 对象(这里只是为了演示。)字符串可以是 providerName ....
然后我们使用这些客户端来调用外部的api。 我想模拟的就是这个
这里是 IClientProvider 接口:
using System.Net.Http;
namespace Multi.Web.Api
{
public interface IClientProvider
{
HttpClient GetClientFor(string providerName);
}
}
然后,我创建了一个中间件(测试中间件)来模拟来自 SUT(在上面)的请求
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System;
using System.Threading.Tasks;
namespace Multi.Web.Api.Test.FakeApi
{
public class FakeExternalApi
{
private readonly RequestDelegate next;
public FakeExternalApi(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
//Mocking the calcapi
if (context.Request.Host.Value.Equals("www.calcapi.io"))
{
if (context.Request.Path.Value == "/count")
{
await context.Response.WriteAsync("1");
}
}
//Mocking the calendarapi
else if (context.Request.Host.Value.Equals("www.calendarapi.io"))
{
if (context.Request.Path.Value == "/today")
{
await context.Response.WriteAsync("2015-04-15");
}
else if (context.Request.Path.Value == "/yesterday")
{
await context.Response.WriteAsync("2015-04-14");
}
else if (context.Request.Path.Value == "/tomorow")
{
await context.Response.WriteAsync("2015-04-16");
}
}
else
{
throw new Exception("undefined host : " + context.Request.Host.Value);
}
await next(context);
}
}
public static class FakeExternalApiExtensions
{
public static IApplicationBuilder UseFakeExternalApi(this IApplicationBuilder app)
{
return app.UseMiddleware<FakeExternalApi>();
}
}
}
这里我模拟来自两个不同主机的请求并监听不同的路径。我也可以做两个中间件,每个主机一个
接下来,我创建了一个使用此 FakeExternalApi
的 TestClientHelper
using Microsoft.AspNet.TestHost;
using Multi.Web.Api.Test.FakeApi;
using System;
using System.Net.Http;
namespace Multi.Web.Api
{
public class TestClientProvider : IClientProvider, IDisposable
{
TestServer _fakeCalendarServer;
TestServer _fakeCalcServer;
public TestClientProvider()
{
_fakeCalendarServer = TestServer.Create(app =>
{
app.UseFakeExternalApi();
});
_fakeCalcServer = TestServer.Create(app =>
{
app.UseFakeExternalApi();
});
}
public HttpClient GetClientFor(string providerName)
{
if (providerName == "calendar")
{
return _fakeCalendarServer.CreateClient();
}
else if (providerName == "calc")
{
return _fakeCalcServer.CreateClient();
}
else
{
throw new Exception("Unsupported external api");
}
}
public void Dispose()
{
_fakeCalendarServer.Dispose();
_fakeCalcServer.Dispose();
}
}
}
它基本上做的是return为我们要求的服务器设置正确的客户端。
现在我可以创建我的测试方法了:
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.DependencyInjection;
using Shouldly;
using Xunit;
using Microsoft.AspNet.Builder;
using System.Net.Http;
namespace Multi.Web.Api
{
public class TestServerHelper : IDisposable
{
public TestServerHelper()
{
ClientProvider = new TestClientProvider();
ApiServer = TestServer.Create((app) =>
{
app.UseServices(services =>
{
services.AddSingleton<IClientProvider>(s => ClientProvider);
});
app.UseMulti();
});
}
public TestClientProvider ClientProvider { get; private set; }
public TestServer ApiServer { get; private set; }
public void Dispose()
{
ApiServer.Dispose();
ClientProvider.Dispose();
}
}
public class MultiMiddlewareTest : IClassFixture<TestServerHelper>
{
TestServerHelper _testServerHelper;
public MultiMiddlewareTest(TestServerHelper testServerHelper)
{
_testServerHelper = testServerHelper;
}
[Fact]
public async Task ShouldReturnToday()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/today");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
String content = await response.Content.ReadAsStringAsync();
Assert.Equal(content, "2015-04-15 count is 1");
}
}
[Fact]
public async Task ShouldReturnYesterday()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/yesterday");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
String content = await response.Content.ReadAsStringAsync();
Assert.Equal(content, "2015-04-14 count is 1");
}
}
[Fact]
public async Task ShouldReturn404()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/someOtherDay");
response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
}
}
}
}
TestServHelper 封装了 Api 和 ClientProvider,这是一个模拟实现,但在生产中它会是一个真正的 ClientProvider 实现,它将 return HttpClient 定位到真正的主机。 (一家工厂)
我不知道这是否是最好的解决方案,但它适合我的需要...还有问题 Xunit.net 需要解决...
我正在尝试使用 TestServer 来测试我的中间件。 在中间件的某处,我的代码正在通过 HttpClient 调用 api。我想通过使用第二个 TestServer 来模拟这个,但想知道这是否可能。我已经尝试过,但出现错误:"Trying to connect ... but the server actively refused it."
代码如下所示:
[Fact]
public async Task Should_give_200_Response()
{
var server = TestServer.Create((app) =>
{
app.UseMiddleware<DummyMiddleware>();
});
var fakeServer = TestServer.Create((app) =>
{
app.UseMiddleware<FakeMiddleware>();
});
using(server)
{
using(fakeServer)
{
var response = await server.CreateClient().GetAsync("/somePath");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
在 DummyMiddleware 代码的某处,我正在做一个
HttpClient client = new HttpClient() { BaseAddress = "http://fakeUrl.com"};
var resp = client.GetAsync("/path/to/api");
url : http://fakeUrl.com would be mocked by the fakeServer. but in fact a real call to http://fakeUrl 是通过网络发布的,但我希望它能命中假服务器,这样我就可以模拟 api.
假设假服务器会模拟 google 日历 api 例如。
更新 : 使用fakeServer 事实上,fakeServer 会听这个 url :“http://fakeUrl.com" and when receive the route "/path/to/api" it would return a json object for instance. What I would like to, is my fakeServer to be returned a mocked object. as a reminder, "http://fakeUrl.com/path/to/api”会在我的代码中的某个地方用 HttpClient 对象调用。
我找到了一个解决方案,但是 visual studio 2015 CTP6 测试有时会通过,有时不会...这似乎是 Xunit 问题,因为当我调试时,一切都很好...但事实并非如此问题在这里
link 到完整代码:github repo
这是我要测试的中间件:
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Multi.Web.Api
{
public class MultiMiddleware
{
private readonly RequestDelegate next;
public MultiMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, IClientProvider provider)
{
HttpClient calendarClient = null;
HttpClient CalcClient = null;
try
{
//
//get the respective client
//
calendarClient = provider.GetClientFor("calendar");
CalcClient = provider.GetClientFor("calc");
//
//call the calendar api
//
var calendarResponse = "";
if (context.Request.Path.Value == "/today")
{
calendarResponse = await calendarClient.GetStringAsync("http://www.calendarApi.io/today");
}
else if (context.Request.Path.Value == "/yesterday")
{
calendarResponse = await calendarClient.GetStringAsync("http://www.calendarApi.io/yesterday");
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
//does not process further
return;
}
//
//call another api
//
var calcResponse = await CalcClient.GetStringAsync("http://www.calcApi.io/count");
//
// write the final response
//
await context.Response.WriteAsync(calendarResponse + " count is " + calcResponse);
await next(context);
}
finally
{
if (calendarClient != null)
{
calendarClient.Dispose();
}
if (CalcClient != null)
{
CalcClient.Dispose();
}
}
}
}
public static class MultiMiddlewareExtensions
{
public static IApplicationBuilder UseMulti(this IApplicationBuilder app)
{
return app.UseMiddleware<MultiMiddleware>();
}
}
}
注意 Invoke 方法正在获取一个 IClientProvider(通过 DI),它将 return 基于一些字符串的不同 HttpClient 对象(这里只是为了演示。)字符串可以是 providerName .... 然后我们使用这些客户端来调用外部的api。 我想模拟的就是这个
这里是 IClientProvider 接口:
using System.Net.Http;
namespace Multi.Web.Api
{
public interface IClientProvider
{
HttpClient GetClientFor(string providerName);
}
}
然后,我创建了一个中间件(测试中间件)来模拟来自 SUT(在上面)的请求
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using System;
using System.Threading.Tasks;
namespace Multi.Web.Api.Test.FakeApi
{
public class FakeExternalApi
{
private readonly RequestDelegate next;
public FakeExternalApi(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
//Mocking the calcapi
if (context.Request.Host.Value.Equals("www.calcapi.io"))
{
if (context.Request.Path.Value == "/count")
{
await context.Response.WriteAsync("1");
}
}
//Mocking the calendarapi
else if (context.Request.Host.Value.Equals("www.calendarapi.io"))
{
if (context.Request.Path.Value == "/today")
{
await context.Response.WriteAsync("2015-04-15");
}
else if (context.Request.Path.Value == "/yesterday")
{
await context.Response.WriteAsync("2015-04-14");
}
else if (context.Request.Path.Value == "/tomorow")
{
await context.Response.WriteAsync("2015-04-16");
}
}
else
{
throw new Exception("undefined host : " + context.Request.Host.Value);
}
await next(context);
}
}
public static class FakeExternalApiExtensions
{
public static IApplicationBuilder UseFakeExternalApi(this IApplicationBuilder app)
{
return app.UseMiddleware<FakeExternalApi>();
}
}
}
这里我模拟来自两个不同主机的请求并监听不同的路径。我也可以做两个中间件,每个主机一个
接下来,我创建了一个使用此 FakeExternalApi
的 TestClientHelper using Microsoft.AspNet.TestHost;
using Multi.Web.Api.Test.FakeApi;
using System;
using System.Net.Http;
namespace Multi.Web.Api
{
public class TestClientProvider : IClientProvider, IDisposable
{
TestServer _fakeCalendarServer;
TestServer _fakeCalcServer;
public TestClientProvider()
{
_fakeCalendarServer = TestServer.Create(app =>
{
app.UseFakeExternalApi();
});
_fakeCalcServer = TestServer.Create(app =>
{
app.UseFakeExternalApi();
});
}
public HttpClient GetClientFor(string providerName)
{
if (providerName == "calendar")
{
return _fakeCalendarServer.CreateClient();
}
else if (providerName == "calc")
{
return _fakeCalcServer.CreateClient();
}
else
{
throw new Exception("Unsupported external api");
}
}
public void Dispose()
{
_fakeCalendarServer.Dispose();
_fakeCalcServer.Dispose();
}
}
}
它基本上做的是return为我们要求的服务器设置正确的客户端。
现在我可以创建我的测试方法了:
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.DependencyInjection;
using Shouldly;
using Xunit;
using Microsoft.AspNet.Builder;
using System.Net.Http;
namespace Multi.Web.Api
{
public class TestServerHelper : IDisposable
{
public TestServerHelper()
{
ClientProvider = new TestClientProvider();
ApiServer = TestServer.Create((app) =>
{
app.UseServices(services =>
{
services.AddSingleton<IClientProvider>(s => ClientProvider);
});
app.UseMulti();
});
}
public TestClientProvider ClientProvider { get; private set; }
public TestServer ApiServer { get; private set; }
public void Dispose()
{
ApiServer.Dispose();
ClientProvider.Dispose();
}
}
public class MultiMiddlewareTest : IClassFixture<TestServerHelper>
{
TestServerHelper _testServerHelper;
public MultiMiddlewareTest(TestServerHelper testServerHelper)
{
_testServerHelper = testServerHelper;
}
[Fact]
public async Task ShouldReturnToday()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/today");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
String content = await response.Content.ReadAsStringAsync();
Assert.Equal(content, "2015-04-15 count is 1");
}
}
[Fact]
public async Task ShouldReturnYesterday()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/yesterday");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
String content = await response.Content.ReadAsStringAsync();
Assert.Equal(content, "2015-04-14 count is 1");
}
}
[Fact]
public async Task ShouldReturn404()
{
using (HttpClient client = _testServerHelper.ApiServer.CreateClient())
{
var response = await client.GetAsync("http://localhost/someOtherDay");
response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
}
}
}
}
TestServHelper 封装了 Api 和 ClientProvider,这是一个模拟实现,但在生产中它会是一个真正的 ClientProvider 实现,它将 return HttpClient 定位到真正的主机。 (一家工厂)
我不知道这是否是最好的解决方案,但它适合我的需要...还有问题 Xunit.net 需要解决...