C# MVC 模型在对 Controller 操作进行单元测试时始终有效

标签 c# asp.net-mvc unit-testing nunit

我是这个 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 在 Controller 内部,我正在实际测试 Controller 。

编辑 2:

这是 Controller 的方法签名:

[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'");
}

这就是整个 Controller 的方法:

[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);
    }
}

最佳答案

你不能单元测试ModelState.IsValid .好吧,你可以,但是你需要额外的代码来完成它,而且它并不是很理想。

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

这是它所在的 NuGet 包:https://www.nuget.org/packages/TestBase-Mvc/

下面是我如何使用它:

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

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

您无法对其进行单元测试的原因是模型验证发生在模型绑定(bind)步骤之前您的 Controller 操作被调用。

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

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

(更复杂的扩展方法可能会让您指定哪个模型键无效。PR 总是受欢迎的)。

关于C# MVC 模型在对 Controller 操作进行单元测试时始终有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41882876/

相关文章:

c# if String 包含 2 "hallo"

c# - 将整数转换为字节

c#范围问题

c# - 包含自己模型类型列表的模型(递归建模?)

c# - 更新查询从 .net 代码返回 0 行。从 SQL Developer 返回 1 行

asp.net-mvc - 使用 Bootstrap,如何为内容创建 2 列? - 包括示例

ios - 电话间隙 HTML 应用程序可以与 ASP.NET MVC Web 服务器建立 session 吗

scala - 如何在 Scala 的测试中分解对象功能

android - 如何禁用/启用网络,在 Android 模拟器中切换到 Wifi?

java - Junit多线程