UnitTest 预计 ModelState 无效,但事实并非如此
UnitTest is expected ModelState to be invalid but it isn't
我有一个 .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)
到 return 行是假的,因为 Name
是必需的 - 然后我将基于此执行我的 Asserts
。但结果为真,所以我们尝试插入一个银行。
我在这里做错了什么?我用 Google 搜索过,但只能找到与单元测试无关的答案。我以为 ModelState
支持 [Required]
属性?
如果我使用 UI 进行测试,我无法创建没有名称的银行 - 它甚至从未到达控制器(如预期的那样)。
根据 docs:
Model validation occurs prior to each controller action being invoked
所以我认为问题在于我如何创建 BanksController
。我是否以错误的方式接近这个测试?我想知道我是否应该在测试中将 ModelState
设置为无效...?
属性是仅在 运行 时被框架识别的元数据,而不是在单元测试期间,因为当应用程序处于 运行 时它们实际上被模型绑定器读取。
要更改状态,您要么必须 运行 集成测试,其中框架的必要部分可用于更新模型状态,
或手动更新模型状态,因为模型绑定未在控制器中 运行ning(尽管集成测试将用于执行模型绑定),以便测试在执行时按预期运行。
[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);
}
Don't try to test model validation or model binding in your unit tests - just test your action method's behavior when confronted with a particular ModelState value.
我有一个 .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)
到 return 行是假的,因为 Name
是必需的 - 然后我将基于此执行我的 Asserts
。但结果为真,所以我们尝试插入一个银行。
我在这里做错了什么?我用 Google 搜索过,但只能找到与单元测试无关的答案。我以为 ModelState
支持 [Required]
属性?
如果我使用 UI 进行测试,我无法创建没有名称的银行 - 它甚至从未到达控制器(如预期的那样)。
根据 docs:
Model validation occurs prior to each controller action being invoked
所以我认为问题在于我如何创建 BanksController
。我是否以错误的方式接近这个测试?我想知道我是否应该在测试中将 ModelState
设置为无效...?
属性是仅在 运行 时被框架识别的元数据,而不是在单元测试期间,因为当应用程序处于 运行 时它们实际上被模型绑定器读取。
要更改状态,您要么必须 运行 集成测试,其中框架的必要部分可用于更新模型状态,
或手动更新模型状态,因为模型绑定未在控制器中 运行ning(尽管集成测试将用于执行模型绑定),以便测试在执行时按预期运行。
[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);
}
Don't try to test model validation or model binding in your unit tests - just test your action method's behavior when confronted with a particular ModelState value.