测试ModelState在asp.net mvc中始终有效

时间:2011-11-17 09:45:35

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

在测试我的控制器动作时,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有效?

7 个答案:

答案 0 :(得分:17)

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

您可以使用

手动验证视图模型
System.ComponentModel.DataAnnotations.Validator.TryValidateObject()

答案 1 :(得分:10)

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

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

将此代码添加到我的单元测试允许它工作:

Configuration

答案 2 :(得分:4)

另一方面。您应该实际测试控制器返回的内容以及返回的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);
}

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

通过这种方式,您可以获得一整套测试,如果您的应用程序爆炸,它将在哪个级别实际显示。模型绑定,控制器或验证。

答案 3 :(得分:3)

  1. 创建控制器类的实例。
  2. 添加模型状态并调用添加模型状态后
  3. modelState总是给出错误的

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

如果要测试验证操作的行为,可以直接添加ModelStateError:

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

答案 6 :(得分:-2)

尝试使用控制器。 ViewModel .ModelState.IsValid而不是controller.ModelState.IsValid。