c# - 测试 .NET Core 控制器视图组件
问题描述
在 .NET Core 中,我们有一个控制器操作,它调用具有依赖注入的 ViewComponent,遵循此处的 Microsoft 文档中的模式。
如果我们有一个返回渲染视图的控制器操作,如下所示:
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 始终为空,因为似乎没有办法将 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
}
解决方案
仅供参考:我认为微软的例子是错误的。一旦我将 View Component 更改为 View 并将数据从 View Component 代码后面移到 Controller 中,那么一切都变得容易测试和实现 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");
}
推荐阅读
- javascript - React,写handleInputChange函数更优雅
- linux - 将命令结果输出到 txt 文件
- vue.js - Vue 无法从库中读取未定义的属性“扩展”
- python - cqlsh 和 Python cassandra 驱动程序中最大时间 uuid 的差异
- python - 有人可以解释为什么 .asp 链接会出现奇怪的行为吗?
- angularjs - AngularJS 1.4 全面升级到 Angular 8。我应该迁移到 1.5 然后升级还是重写?
- c++ - 我的函数因式分解程序存在变量问题,显示不正确的答案
- python - 如何拉伸 matplotlib 频谱图的 x 轴?
- r - valueBox 显示使用反应函数从 selectInput 中选择的列的最大值
- c# - 按下 ctrl+space 时不要触发单击焦点元素