【问题标题】:Testing ModelState is always valid in asp.net mvc测试 ModelState 在 asp.net mvc 中始终有效
【发布时间】:2011-12-31 04:34:00
【问题描述】:

在测试我的控制器的操作时,ModelState 始终有效。

public class Product
{
    public int Id { get; set; }

    [Required]
    [StringLength(10)]
    public string Name { get; set; }

    [Required]
    public string Description { get; set; }

    [Required]
    public decimal Price { get; set; }
}

还有我的控制器。

public class ProductController : Controller
{
      [HttpPost]
      public ActionResult Create(Product product)
      {
            if (ModelState.IsValid)
            {
                   // Do some creating logic...
                   return RedirectToAction("Display");
            }

             return View(product);              
      }
 }

并测试:

[Test]
public TestInvalidProduct()
{
     var product = new Product();
     var controller = new ProductController();
     controller.Create(product);
     //controller.ModelState.IsValid == true
}

当产品没有名称、描述和价格时,为什么modelState有效?

【问题讨论】:

  • 您将控制器操作作为普通方法调用,而不是通过 MVC“堆栈”进行模型绑定和验证。
  • @RichardD 那么我如何知道模型状态在动作中完成工作?我必须用单元测试来测试它
  • 我不知道 :D 这就是为什么我没有将此作为答案发布。 dskh 的回答可能会对您有所帮助。
  • Unit tests on MVC validation 的可能重复项
  • 如果您的控制器的逻辑依赖于任何错误的存在,您应该通过在其 ModelState 中添加测试错误来安排控制器。您的模型的实际验证应单独测试。当然,如果您的控制器应该对特定错误做出反应,只需将该错误添加到 ModelState

标签: c# asp.net-mvc asp.net-mvc-3 unit-testing modelstate


【解决方案1】:

验证发生在发布的数据绑定到视图模型时。然后将视图模型传递给控制器​​。您正在跳过第 1 部分并将视图模型直接传递给控制器​​。

您可以使用手动验证视图模型

System.ComponentModel.DataAnnotations.Validator.TryValidateObject()

【讨论】:

  • 您能否举例说明在这种情况下如何使用 DataAnnotations.Validator.TryValidateObject() 来测试产品
  • 这篇文章(来自 Richard D 的链接问题)给出了一个很好的例子:stackoverflow.com/questions/1269713/…
【解决方案2】:

I have come across the same issue 虽然这里接受的答案确实解决了“无验证”问题,但它确实给我留下了很大的负面影响:当出现验证错误时它会抛出异常,而不是简单地设置 ModelState.Invalidfalse

我只在 Web Api 2 中对此进行了测试,因此我不知道哪些项目将提供此功能,但有一种方法 ApiController.Validate(object) 强制对传递的对象进行验证,并且仅将 ModelState.IsValid 设置为 false。此外,您还必须实例化 Configuration 属性。

将此代码添加到我的单元测试中使其能够工作:

userController.Configuration = new HttpConfiguration();
userController.Validate(addressInfo);

【讨论】:

    【解决方案3】:

    在另一个笔记上。您应该实际测试控制器返回的内容以及返回的 ActionResult 是否符合您的预期。应该单独测试 ModelBinder。

    假设您想切换到自定义模型绑定器。您可以将 ModelBinder 测试重用于您正在创建的新 ModelBinder。如果您的业务规则保持不变,您应该能够直接重用相同的测试。但是,如果您混合使用 Controller 测试和 ModelBinder 测试并且测试失败,您不知道问题出在 Controller 还是 ModelBinder。

    假设您像这样测试您的模型绑定:

    [Test]
    public void Date_Can_Be_Pulled_Via_Provided_Month_Day_Year()
    {
        // Arrange
        var formCollection = new NameValueCollection { 
            { "foo.month", "2" },
            { "foo.day", "12" },
            { "foo.year", "1964" }
        };
    
        var valueProvider = new NameValueCollectionValueProvider(formCollection, null);
        var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(FwpUser));
    
        var bindingContext = new ModelBindingContext
        {
            ModelName = "foo",
            ValueProvider = valueProvider,
            ModelMetadata = modelMetadata
        };
    
        DateAndTimeModelBinder b = new DateAndTimeModelBinder { Month = "month", Day = "day", Year = "year" };
        ControllerContext controllerContext = new ControllerContext();
    
        // Act
        DateTime result = (DateTime)b.BindModel(controllerContext, bindingContext);
    
        // Assert
        Assert.AreEqual(DateTime.Parse("1964-02-12 12:00:00 am"), result);
    }
    

    现在您知道,您的模型已正确绑定,您可以在单独的测试中继续使用控制器测试模型,以检查它是否返回正确的结果。此外,您可以使用绑定的模型值来测试您的验证属性。

    通过这种方式,您可以获得一整套测试,如果您的应用程序发生故障,这些测试将揭示它实际发生故障的级别。模型绑定、控制器或验证。

    【讨论】:

    • +1,模型绑定和验证应该与控制器逻辑分开测试
    【解决方案4】:
    1. 创建控制器类的实例。
    2. 添加模型状态并调用添加模型状态后
    3. modelState 总是给 false

      controller.ModelState.AddModelError("key", "error message");
      
      var invalidStateResult = _controller.Index();
      
      Assert.IsNotNull(invalidStateResult);
      

    【讨论】:

    • 比你这么多我的朋友 :)
    【解决方案5】:

    使用controller.UpdateModelcontroller.TryUpdateModel 使用控制器当前的ValueProvider 绑定一些数据并在检查ModelState.IsValid 之前触发模型绑定验证

    【讨论】:

      【解决方案6】:

      如果您想测试验证操作的行为,您可以简单地添加 ModelStateError:

      ModelState.AddModelError("Password", "The Password field is required");
      

      【讨论】:

      • 但是...如果我在测试中更改代码,而不是测试代码,它会测试代码...这不是我要问的。不过谢谢。
      • 否则你在测试 MVC 框架,而不是你自己的代码。如果只有你有自己的验证检查代码,如果有,你应该测试它而不是控制器的动作......快速问题:你想测试什么:它会发现错误吗?或者如果出现错误会发生什么?
      • 它会发现错误吗,因为我有一个应该引发错误的插件(FluentValidation
      【解决方案7】:

      试试 controller.ViewModel.ModelState.IsValid 而不是 controller.ModelState.IsValid。

      【讨论】:

      • 实际上我没有controller.ViewModel
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2019-10-24
      • 2017-05-30
      • 2015-07-09
      • 2021-09-10
      • 2011-01-23
      • 2016-09-30
      相关资源
      最近更新 更多