测试.NET Core Controller视图组件

时间:2018-06-22 20:56:16

标签: c# asp.net-core asp.net-core-2.0

在.NET Core中,按照Microsoft文档here中的模式,我们有一个控制器动作,该动作调用具有依赖项注入的ViewComponent。

如果我们有一个控制器操作返回如下所示的渲染视图:

public class MyController : Controller
{
    private readonly IToDoContext _todoContext;
    public MyController(IToDoContext todoContext)
    {
        _todoContext = todoContext;
    }

    public IActionResult IndexVC()
    {
        return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
    }
}

还有一个具有这样的依赖项注入的ViewComponent:

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }
    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db.ToDo.Where(x => x.IsDone == isDone && x.Priority <= maxPriority).ToListAsync();
    }
}

在控制器操作单元测试中,我们如何将ToDoContext注入到ViewComponent中?到目前为止,result.Model始终为null,因为似乎没有办法将ToDoContext db注入Controller,然后注入ViewComponent。

[Fact]
public void Should_return_viewcomponent()
{
    var mockToDoContext = new Mock<IToDoContext>();
    mockToDoContext.Setup(m => m.Get()).ReturnsAsync(new ToDoContext());

    var sut = new MyController(mockToDoContext.Object);

    var result = sut.PriorityListViewComponent("myparams") as ViewComponentResult;
    // ToDoContext db is never set, so the result.Model is always null
}

1 个答案:

答案 0 :(得分:0)

仅供参考:我认为微软的例子是错误的。一旦将View Component更改为View并将数据从View Component代码中获取到的数据移入控制器后,一切都变得易于测试和实现Bad Request逻辑,依此类推。只需记住将View Component从View Component目录中移出,并在视图中将layout设置为null。

控制器应该看起来像这样:

public class MyController : Controller
{
    private readonly IToDoContext _db;
    public MyController(IToDoContext db)
    {
        _db = db;
    }

    public async Task<IActionResult> IndexVC("PriorityList", new { maxPriority, isDone = false })
        {
            if (string.IsNullOrWhiteSpace(maxPriority))
                return BadRequest($"maxPriority cannot be empty");

            var model = await InvokeAsync(maxPriority, isDone);

            if (model == null)
            {
                return BadRequest($"model not found");
            }

            return View(model);
        }

    public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return _db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
}

单元测试如下:

[Fact]
public void Should_return_viewcomponent()
{
    var mockToDoContext = new Mock<IToDoContext>();
    mockToDoContext.Setup(m => m.Get())
        .ReturnsAsync(new ToDoContext());

    var sut = new MyController(mockToDoContext.Object);

    var result = await sut.PriorityList("myparams") as ViewResult;
    result.Model.ShouldNotBeNull();
    var resultModel = result.Model as PriorityListModel;
    resultModel.ShouldNotBeNull();
    resultModel.Thing.ShouldBe("whatever");
}

错误请求单元测试如下所示(请注意BadRequestObjectResult):

[Fact]
public async Task Should_return_bad_request_if_result_not_found()
{
    var mockToDoContext = new Mock<IToDoContext>();
    mockToDoContext.Setup(m => m.Get())
        .ReturnsAsync(null);

    var httpContext = new DefaultHttpContext();

    var sut = new MyController(mockToDoContext.Object)
    {
        ControllerContext = new ControllerContext { HttpContext = httpContext }
    };
    var result = await sut.PriorityList("myparams") as BadRequestObjectResult;

    result.ShouldNotBeNull();
    result.StatusCode.ShouldBe(400);
    result.Value.ShouldBe("model not found");
}