我有一个.NetCore MVC项目,正在尝试对控制器进行单元测试。
ViewModel (请注意[Required]
属性):
public class Bank : BaseObject
{
[Required]
[DisplayName("Bank")]
public string Name { get; set; }
}
控制器操作:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Name")] Bank bank)
{
if (ModelState.IsValid)
{
await _bankService.Insert(bank);
return RedirectToAction(nameof(Index));
}
return View(bank);
}
单元测试:
[Test]
public async Task When_PostingCreateBankThatIsInvalid_ThenBankIsReturned()
{
var bank = new Bank
{
//nothing set = invalid state - this is what we want
};
var controller = new BanksController(null);
var response = await controller.Create(bank);
}
本来我期望if (ModelState.IsValid)
行会返回false,因为需要Name
-然后我将基于此执行Asserts
。但是结果是正确的,因此我们尝试插入银行。
我在这里做错了什么?我已经在Google上搜索过,但是只能找到与单元测试无关的答案。我以为ModelState
支持[Required]
属性?
如果我使用UI进行测试,则无法创建没有名称的银行-银行甚至都无法到达控制器(如预期)。
根据docs:
在每个控制器动作被调用之前进行模型验证
所以我相信问题在于我如何创建BanksController
。我是否以错误的方式参加了此测试?我想知道我是否应该在测试中将ModelState
设置为无效...?
答案 0 :(得分:2)
属性是仅在运行时由框架识别的元数据,而在单元测试期间则无法识别,因为模型绑定程序实际上是在应用程序运行时识别它们的。
要更改状态,您将不得不运行集成测试,其中框架的必要部分可用以更新模型状态,
或手动更新模型状态,因为控制器中的模型绑定未运行(尽管将使用集成测试来执行模型绑定),因此测试执行时其行为与预期的一样。
[Test]
public async Task When_PostingCreateBankThatIsInvalid_ThenBankIsReturned() {
//Arrange
var bank = new Bank
{
//nothing set = invalid state - this is what we want
};
var controller = new BanksController(null);
controller.ModelState.AddModelError("Name","Name required");
//Act
var response = await controller.Create(bank);
//Assert
response.Should().NotBeNull()
.And.BeOfType<ViewResult>();
var viewResult = response as ViewResult;
viewResult.Model.Should().Be(model);
}
不要尝试在单元测试中测试模型验证或模型绑定-只需在遇到特定的ModelState值时测试操作方法的行为即可。