Я создаю основное веб-приложение asp.net, которое создает маршруты из определенных пользователем источников (конфигурация, база данных и т. Д.). Я использовал подход на основе фабрики промежуточного программного обеспечения, который отлично работает, за исключением того, что я не знаю, как изолировать промежуточное программное обеспечение для модульного / интеграционного тестирования. Я использую данные маршрута из HttpContext для проверки входящего запроса на соответствие определенному пользователем источнику конфигурации, поэтому мне нужно, чтобы HttpContext имел RouteData, поэтому я склоняюсь к тестированию интеграции с xUnit.
Если я делаю запрос, используя httpclient, созданный с помощью AspNetCore.TestHost.TestServer.CreateCLient (), я прохожу свой путь через всю цепочку вызовов, чего я и ожидал. Что мне нужно сделать, так это завершить работу промежуточного программного обеспечения или предоставить HttpContext с помощью RouteData.
Я пробовал уродливый модульный тест с использованием AutoFixture и DefaultHttpContext (), но, как и ожидалось, я не получаю RouteData в моем HttpContext, поэтому мои тесты никогда не пройдут.
ПО промежуточного слоя
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{
if (_flowApis.Count == 0)
throw new ArgumentNullException();
var doesMatch = false;
var routeData = context.GetRouteData();
foreach (var api in _flowApis)
{
if (!RequestType.IsValidRequestType(api.Request.Type))
throw new InvalidOperationException();
else
foreach (Route route in routeData.Routers.Where(r => r.GetType() == typeof(Route)))
{
if (route.RouteTemplate == api.Route)
{
if (route.Constraints.TryGetValue("httpMethod", out IRouteConstraint routeConstraint))
{
var value = (HttpMethodRouteConstraint)routeConstraint;
if (value.AllowedMethods.Contains(api.Method))
{
doesMatch = true;
var routeValues = GetRouteValues(routeData);
var request = new Core.Request(context.Request, api, routeValues);
context.Items.Add(nameof(Core.Request), request);
break;
}
}
}
}
if (doesMatch)
break;
}
if (!doesMatch)
{
context.Response.StatusCode = 404;
await context.Response.WriteAsync("", new CancellationToken());
}
else
await next(context);
}
catch(ArgumentNullException ex)
{
var mex = new MiddleWareException<MatchRouteToFlowApiMiddleware>("Flow Configuration in app.settings has not been set.", ex);
context.Response.StatusCode = 500;
await context.Response.WriteAsync("", new CancellationToken());
}
catch (InvalidOperationException ex)
{
var mex = new MiddleWareException<MatchRouteToFlowApiMiddleware>("Invalid Request Type", ex);
context.Response.StatusCode = 500;
await context.Response.WriteAsync("", new CancellationToken());
}
}
Модульный тест
[TestMethod]
public async Task Request_path_matches_an_api_route_and_method()
{
var fixture = ScaffoldFixture();
var flowApi = fixture.Build<FlowApi>()
.With(p => p.Route, "/some/path")
.With(m => m.Method, "GET")
.Create();
var flowApiRequest = fixture.Build<Request>()
.With(t => t.Type, "CData")
.Create();
flowApi.Request = flowApiRequest;
var flowConfiguration = fixture.Create<FlowConfiguration>();
flowConfiguration.FlowApis.Clear();
flowConfiguration.FlowApis.Add(flowApi);
var middleware = new MatchRouteToFlowApiMiddleware(flowConfiguration: flowConfiguration);
var context = new DefaultHttpContext();
context.Request.Method = "GET";
context.Request.Path = "/some/path";
context.Response.Body = new MemoryStream();
await middleware.InvokeAsync(context, (httpContext) => Task.FromResult(0));
context.Items.ContainsKey(nameof(Core.Request)).Should().BeTrue();
}
Интеграционный тест
[Fact]
public async Task Request_path_matches_an_api_route_and_method()
{
//Arrange
var response = await _httpClient.GetAsync("/some/route");
response.StatusCode.Should().Be(500);
}
При выполнении модульного теста я никогда не могу удовлетворить внутреннюю часть для каждого, потому что у меня нет данных маршрута в HttpContext.
При выполнении интеграционного теста промежуточное ПО выполняется так, как ожидалось, но, поскольку оно не завершается, я не могу проверить