如何对用ServiceFilterAttribute和自定义ActionFilter实现装饰的控制器进行单元测试

科比

总结

  • 我正在尝试使用ActionFilter实现测试控制器
  • 单元测试失败,因为没有在单元测试中调用ActionFilter。
  • 通过邮递员进行的测试按预期工作,并获得了正确的结果。
  • 可以像这样对控制器进行测试,还是应该进行集成测试?

细目

我可以在单元测试中单独测试ActionFilter,我想做的就是在单元测试中测试控制器。

动作过滤器如下所示:

 public class ValidateEntityExistAttribute<T> : IActionFilter
        where T : class, IEntityBase
    {
        readonly AppDbContext _appDbContext;
        public ValidateEntityExistAttribute(AppDbContext appDbContext)
        {
            this._appDbContext = appDbContext;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {}

        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ActionArguments.ContainsKey("id"))
            {
                context.Result = new BadRequestObjectResult("The id must be passed as parameter");
                return;
            }

            int id = (int)context.ActionArguments["id"];

            var foundEntity = _appDbContext.Set<T>().Find(id);
            if (foundEntity == null)
                context.Result = new NotFoundResult();
            else
                context.HttpContext.Items.Add("entity_found", foundEntity);
        }
    }

ActionFilter实现已添加到启动文件中的服务中

ConfigureServices(IServiceCollection services)
{
...
 services.AddScoped<ValidateEntityExistAttribute<Meeting>>();
...
}

该过滤器可以应用于需要检查实体是否存在的任何控制器方法。即GetById方法。

[HttpGet("{id}")]
[ServiceFilter(typeof(ValidateEntityExistAttribute<Meeting>))]
public async Task<ActionResult<MeetingDto>> GetById(int id)
    {            
        var entity = HttpContext.Items["entity_found"] as Meeting;
        await Task.CompletedTask;
        return Ok(entity.ConvertTo<MeetingDto>());
    }

在xUnit测试中,我已经设置了测试来测试控制器,如下所示:

[Fact]
public async Task Get_Meeting_Record_By_Id()
{
    // Arrange
    var _AppDbContext = AppDbContextMocker.GetAppDbContext(nameof(Get_All_Meeting_Records));
    var _controller = InitializeController(_AppDbContext);

    //Act
    
    var all = await _controller.GetById(1);

    //Assert
    Assert.Equal(1, all.Value.Id);

    //clean up otherwise the other test will complain about key tracking.
    await _AppDbContext.DisposeAsync();
}

这就是InitializeController方法的样子,我留下了注释行,以便对我尝试的内容可见,没有注释的代码起作用。我嘲笑并使用默认类。

private MeetingController InitializeController(AppDbContext appDbContext)
    {

        var _controller = new MeetingController(appDbContext);

        var spf = new DefaultServiceProviderFactory(new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true });
        var sc = spf.CreateBuilder(new ServiceCollection());

        sc.AddMvc();
        sc.AddControllers();
        //(config =>
        //{
        //    config.Filters.Add(new ValidateModelStateAttribute());
        //    config.Filters.Add(new ValidateEntityExistAttribute<Meeting>(appDbContext));
        //});
        
        sc.AddTransient<ValidateModelStateAttribute>();
        sc.AddTransient<ValidateEntityExistAttribute<Meeting>>();

        var sp = sc.BuildServiceProvider();

        //var mockHttpContext = new Mock<HttpContext>();
        var httpContext = new DefaultHttpContext
        {
            RequestServices = sp
        };
        //mockHttpContext.Setup(cx => cx.RequestServices).Returns(sp);

        //var contDesc = new ControllerActionDescriptor();
        //var context = new ControllerContext();
        //var context = new ControllerContext(new ActionContext(mockHttpContext.Object, new RouteData(), contDesc));

        //context.HttpContext = mockHttpContext.Object;
        //context.HttpContext = httpContext;
        //_controller.ControllerContext = context;
        _controller.ControllerContext.HttpContext = httpContext;

        return _controller;
    }

的问题是,在运行单元测试时,永远不会调用ActionFilter实现,因此会破坏测试,因为var entity = HttpContext.Items["entity_found"] as Meeting;在控制器中始终为null更准确地说,HttpContext.Items它始终为null。

ActionFilter中的断点永远不会命中。

通过邮递员进行测试时,一切都按预期工作,并且断点被击中

有没有办法以这种方式将控制器作为单元测试进行测试,还是现在应该将此测试移至集成

科比

感谢@Fei Han提供有关单元测试控制器的链接

事实证明,这是设计使然,如Microsoft文档所述

单元测试控制器 设置控制器动作的单元测试,以 关注控制器的行为。控制器单元测试避免了 诸如过滤器,路由和模型绑定之类的情况。该测试 涵盖统称为响应组件之间的交互 请求,由集成测试处理。

因此,测试应转到集成测试。

这种情况下,该GetById(int id)方法没有详细的实现,因此对其进行单元测试几乎没有价值

如果GetById(int id)方法的实现更复杂,或者ActionFilter不能阻止进一步的处理,HttpContext.Items["entity_found"]则应模拟。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在ConfigureServices中使用自定义HttpClient和Polly策略进行单元测试核心API控制器

如何使用Jasmine(Angular JS)对自定义装饰器进行单元测试

如何对返回模型的控制器进行单元测试?

如何对Symfony控制器进行单元测试

如何对iOS视图控制器进行单元测试?

如何在装饰有[ApiController]的控制器中进行单元测试模型验证?

对自定义STL兼容的容器和迭代器进行单元测试

如何对自定义的Jackson JsonSerializer进行单元测试?

如何对自定义Wicket组件进行单元测试

如何对自定义JsonConverter进行单元测试

如何使用 spring boot 和 mockito 对控制器方法进行单元测试

如何在Cordapp中对服务和控制器(kotlin)进行单元测试?

Angular2如何对自定义验证器指令进行单元测试?

如何使用Java对自定义日期反序列化器进行单元测试

如何对自定义上下文管理器进行单元测试?

返回自定义结果的ASP.NET Web API 2控制器的单元测试

如何对作为跟踪源对象包装器的自定义记录器进行单元测试

在实现Spring控制器的单元测试时缺少hasSize()和hasProperty()依赖项

对使用Moment JS的控制器进行单元测试

尝试使用 $state 对控制器进行单元测试

在Play 2.6中对控制器进行单元测试

对MVC控制器进行单元测试

使用NUnit进行单元测试DateTime控制器

自定义验证器属性在单元测试中有效,但在 web api 控制器中无效?

使用Spring Boot,Kotlin和Junit进行休息控制器单元测试

使用 xunit 和 moq 进行 net core api 控制器单元测试

对自定义Web API AuthorizeAttribute进行单元测试

对ngx形式的自定义模板进行单元测试

Spring Batch:对自定义项目阅读器进行单元测试