Я взломал его после прочтения: https://stackoverflow.com/a/50817536/1403748
Хотя это не дает прямого ответа на мой вопрос, оно поставило меня на правильный путь.Ключом была область видимости класса MyFilterImpl
.Подняв его, тестовые задания фильтра можно отделить от контроллера, который он увеличивает.
public class MyFilter : TypeFilterAttribute
{
public MyFilter() : base(typeof(MyFilterImpl))
{
}
}
public class MyFilterImpl : IActionFilter
{
private readonly IDependency _dependency;
public MyFilterImpl(IDependency injected)
{
_dependency = injected;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_dependency.DoThing();
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
Как только это будет сделано, нужно просто создать экземпляр ActionExecutingContext
и напрямую вызвать .OnActionExecuting()
дляэкземпляр фильтра.Я закончил тем, что написал вспомогательный метод, который позволяет IServiceCollection
быть переданным в него (требуется, чтобы гарантировать, что тестовые сервисы / данные могут быть введены во время теста):
/// <summary>
/// Triggers IActionFilter execution on FakeApiController
/// </summary>
private static async Task<HttpContext> SimulateRequest(IServiceCollection services, string methodName)
{
var provider = services.BuildServiceProvider();
// Any default request headers can be set up here
var httpContext = new DefaultHttpContext()
{
RequestServices = provider
};
// This is only necessary if MyFilterImpl is examining the Action itself
MethodInfo info = typeof(FakeApiController)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(x => x.Name.Equals(methodName));
var actionContext = new ActionContext
{
HttpContext = httpContext,
RouteData = new RouteData(),
ActionDescriptor = new ControllerActionDescriptor()
{
MethodInfo = info
}
};
var actionExecutingContext = new ActionExecutingContext(
actionContext,
new List<IFilterMetadata>(),
new Dictionary<string, object>(),
new FakeApiController()
{
ControllerContext = new ControllerContext(actionContext),
}
);
var filter = new MyFilterImpl(provider.GetService<IDependency>());
filter.OnActionExecuting(actionExecutingContext);
await (actionExecutingContext.Result?.ExecuteResultAsync(actionContext) ?? Task.CompletedTask);
return httpContext;
}
Сам метод теста простостановится примерно так:
[Fact]
public void MyFilterTest()
{
IServiceCollection services = new ServiceCollection();
services.AddScoped<IDependency, MyDependency>();
var httpContext = await SimulateRequest(services, "Ping");
Assert.Equal(403, httpContext.Response.StatusCode);
}
Надеюсь, это будет кому-то полезно: -)