繁体   English   中英

当单元测试控制器动作时,C#MVC模型始终有效

[英]C# MVC Model is always valid when Unit Testing a Controller Action

我是这个C#世界中的初学者。

有这个模型类:

    public class Fund
    {
        [Required]
        public int id { get; set; }
        [Required]
        public string name { get; set; }
        [Required]
        public string nickname { get; set; }
    }

尽管[Required]批注覆盖了所有属性,但实例类似于:

Fund f = new Fund();
f.name = "test name";
f.nickname = "test ninckname";

始终通过以下测试:

if (ModelState.IsValid)
{
    // do stuff
}

我应该如何设置模型,以使类似的实例不会在ModelState.IsValid测试中通过?

其他实例如:

Fund f1 = new Fund();
f1.id = 3;
f1.nickname = "test ninckname";

Fund f2 = new Fund();
f2.id = 3;
f2.name = "test name";

也通过了测试。

编辑:

ModelState.IsValid在控制器内部,我实际上正在测试控制器。

编辑2:

那就是控制器的方法签名:

[HttpPatch]
public ActionResult EditFund(Fund fund)

编辑3:这是我的整个测试方法:

[TestMethod]
public void TestEditInvalidFund()
{
    // Arrange
    FundController controller = new FundController();
    controller.ControllerContext = TestModelHelper.AdminControllerContext();
    var _fund = new Mock<IFund>();
    _fund.SetupGet(f => f.id).Returns(1);
    //_fund.SetupGet(f => f.name).Returns("Fund name");
    _fund.SetupGet(f => f.nickname).Returns("Fund nickname");
    _fund.Setup(f => f.Edit()).Callback(() => {}).Verifiable();

    // Act
    var result = (JsonResult)controller.EditFund(_fund.Object);

    // Assert
    SimpleMessage resultMessage = m_serializer.Deserialize<SimpleMessage>(m_serializer.Serialize(result.Data));
    Assert.IsNotNull(resultMessage.status, "JSON record does not contain 'status' required property.");
    Assert.IsTrue(resultMessage.status.Equals("fail"), "status must be 'fail'");
}

这就是整个控制器的方法:

[HttpPatch]
public ActionResult EditFund(IFund _fund)
{
    try
    {
        if (ModelState.IsValid)
        {
            //_fund.Edit();
        }
        else
        {
            string error_messages = "";
            foreach (var e in ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList())
            {
                error_messages += e[0].ErrorMessage + "\n";
            }
            throw new Exception(error_messages);
        }
        return MessageHelper(@Resources.Global.success, @Resources.Funds.success_editing_fund, "success");
    }
    catch (Exception err)
    {
        return ErrorHelper(@Resources.Global.error, @Resources.Funds.error_editing_fund, err.Message);
    }
}
[Required]
public int id { get; set; }

由于int不可为空,因此这将始终有效。 默认值为0。

您无法对ModelState.IsValid单元测试。 好吧,可以,但是您需要额外的代码才能做到这一点,而且它也不是很理想。

这是我在github上的代码: WithModelStateIsInvalid<TController>()

这是它位于其中的NuGet软件包: https : //www.nuget.org/packages/TestBase-Mvc/

这是我的用法:

UnitUnderTest
    .WithModelStateIsInValid()
    .Action(model)
    .ShouldBeViewResult()
    .ShouldBeAnInvalidModel();

———————————————————————————————————————————

无法对其进行单元测试的原因是,在调用控制器操作之前 ,模型验证发生在ModelBinding步骤中。

因此,要模拟模型验证失败,最简单的方法是在调用操作之前,在单元测试代码中“手动”使controller.ModelState无效。 扩展方法是做什么的,但实际上只是一行:

controller.ModelState.AddModelError("FieldName", @"error message")

(更复杂的扩展方法可能使您可以指定哪个Model密钥无效。始终欢迎PR)。

不确定为什么有效,但是您可以尝试通过以下方式显式地重新验证模型:

TryValidateModel(fund)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM