我有一个简单的TypeFilterAttribute
,名为MyFilter
,它在后台(.net核心2.2)利用依赖项注入:
public class MyFilter : TypeFilterAttribute
{
public MyFilter() :
base(typeof(MyFilterImpl))
{
}
private 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)
{
}
}
}
我正尝试使用看起来像这样的FakeController
与xUnit进行测试:
[ApiController]
[MyFilter]
public class FakeApiController : ControllerBase
{
public FakeApiController()
{
}
[HttpGet("ping/{pong}")]
public ActionResult<string> Ping(string pong)
{
return pong;
}
}
我遇到的问题是,我似乎无法在 测试时间触发MyFilter
逻辑。 测试方法到目前为止看起来像这样:
[Fact]
public void MyFilterTest()
{
IServiceCollection services = new ServiceCollection();
services.AddScoped<IDependency, InMemoryThing>();
var provider = services.BuildServiceProvider();
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = provider;
var actionContext = new ActionContext
{
HttpContext = httpContext,
RouteData = new RouteData(),
ActionDescriptor = new ControllerActionDescriptor()
};
var controller = new FakeApiController()
{
ControllerContext = new ControllerContext(actionContext)
};
var result = controller.Ping("hi");
}
知道我在这里缺少什么吗?
谢谢!
答案 0 :(得分:0)
在实现自定义验证过滤器时,我遇到了同样的问题,我发现APIController属性执行自动模型状态验证,因此 从Controller中删除apiController属性,或者通过将SuppressModelStateInvalidFilter选项设置为true来禁用默认行为的更好方法。您可以在ConfigureServices方法中将此选项设置为true。喜欢,
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}
答案 1 :(得分:0)
我在阅读以下内容后将其破解: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);
}
希望这对其他人有用:-)