如何在 asp.net core 中对 web api 的操作过滤器属性进行单元测试?

Jef*_*123 5 actionfilterattribute asp.net-core-mvc

我为 web api 编写了一个动作过滤器。如果 api 控制器中的方法抛出未处理的异常,则过滤器会创建内部错误 500 响应。

我需要知道如何测试过滤器?

我进行了广泛的研究,但无法创建合适的测试。我尝试了上下文模拟、服务定位器实现,甚至使用测试服务器进行集成测试。

Web api 控制器如下所示:

namespace Plod.Api.ApiControllers
{
    [TypeFilter(typeof(UnhandledErrorFilterAttribute))]
    [Route("api/[controller]")]
    public class GamesController : BaseApiController
    {
        public GamesController(IGameService repository, 
            ILogger<GamesController> logger,
            IGameFactory gameFactory
            ) : base(
                repository, 
                logger,
                gameFactory
                )
        { }

        // ..... controller methods are here
    }
}
Run Code Online (Sandbox Code Playgroud)

这里可以找到完整的控制器。

过滤器是这样的:

namespace Plod.Api.Filters
{
    public class UnhandledErrorFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Exception != null)
            {
                filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                filterContext.ExceptionHandled = true;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我什至欢迎对过滤器实现的更改作为可能的解决方法。任何帮助或想法将不胜感激。谢谢。

Mel*_*per 3

你可能不能。但是,您可以做的是启动 a TestServer,然后使用 HttpClient 来访问它。这确实是集成测试而不是单元测试。然而,这是一种很好的集成测试,因为它可以在管道中安全地运行。

本文档说明了如何执行此操作: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests ?view=aspnetcore-3.1

您将面临的问题是您需要模拟应用程序内的底层服务。如果您不这样做,您的整个服务器将启动并尝试访问数据库等。这是一个示例。这是使用起订量。顺便说一句,我ConfigureServices与单元测试共享该方法,因此它们使用模拟服务的相同对象网格。您仍然可以使用 Moq 或 NSubstitute 的完整功能来测试后端(甚至前端)。

我可以在测试中使用断点来命中我的属性。

private void ConfigureServices(IServiceCollection services)
{
    var hostBuilder = new WebHostBuilder();
    hostBuilder.UseStartup<TestStartup>();
    hostBuilder.ConfigureServices(services =>
    {
        ConfigureServices(services);
    });

    _testServer = new TestServer(hostBuilder);
    _httpClient = _testServer.CreateClient();
}


private void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(_storageManagerFactory.Object);
    services.AddSingleton(_blobReferenceManagerMock.Object);
    services.AddSingleton(_ipActivitiesLoggerMocker.Object);
    services.AddSingleton(_loggerFactoryMock.Object);
    services.AddSingleton(_hashingService);
    services.AddSingleton(_settingsServiceMock.Object);
    services.AddSingleton(_ipActivitiesManager.Object);
    services.AddSingleton(_restClientMock.Object);
    _serviceProvider = services.BuildServiceProvider();
}


public class TestStartup 
{
    public void Configure(
        IApplicationBuilder app,
        ISettingsService settingsService)
    {
        app.Configure(settingsService.GetSettings());
    }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        var mvc = services.AddMvc(option => option.EnableEndpointRouting = false);
        mvc.AddApplicationPart(typeof(BlobController).Assembly);
        services.AddSingleton(new Mock<IHttpContextAccessor>().Object);
        return services.BuildServiceProvider();
    }
}
Run Code Online (Sandbox Code Playgroud)