【问题标题】:ASP.NET MVC Controller post method unit test: ModelState.IsValid always trueASP.NET MVC 控制器发布方法单元测试:ModelState.IsValid 始终为真
【发布时间】:2014-03-21 14:41:12
【问题描述】:

我已经为 ASP.NET MVC Web 应用程序编写了我的第一个单元测试。一切正常,它为我提供了有价值的信息,但我无法测试视图模型中的错误。 ModelState.IsValid 始终为 true,即使某些值未填写(空字符串或 null)。

我已经读过模型验证发生在发布的数据映射到模型时,您需要编写一些代码来自己进行模型验证:

我已经尝试了链接网页中提供的三个示例,但似乎对我不起作用。

一些代码:

我的视图模型

...
[Required(ErrorMessageResourceName = "ErrorFirstName", ErrorMessageResourceType = typeof(Mui))]
[MaxLength(50)]
[Display(Name = "Firstname", ResourceType = typeof(Mui))]
public string FirstName { get; set; }
...

控制器

...
 [HttpPost]
    public ActionResult Index(POSViewModel model)
    {
        Contract contract = contractService.GetContract(model.ContractGuid.Value);

        if (!contract.IsDirectDebit.ToSafe())
        {
            ModelState.Remove("BankName");
            ModelState.Remove("BankAddress");
            ModelState.Remove("BankZip");
            ModelState.Remove("BankCity");
            ModelState.Remove("AccountNr");
        }

        if (ModelState.IsValid)
        {
            ...

            contractValidationService.Create(contractValidation);
            unitOfWork.SaveChanges();

            return RedirectToAction("index","thanks");
        }
        else
        {
            return Index(model.ContractGuid.ToString());
        }
    }

我的单元测试

  posViewModel.FirstName = null;
  posViewModel.LastName = "";
 ...
 var modelBinder = new ModelBindingContext()
        {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => posViewModel, posViewModel.GetType()),
            ValueProvider = new NameValueCollectionValueProvider(new System.Collections.Specialized.NameValueCollection(), CultureInfo.InvariantCulture)
        };
        var binder = new DefaultModelBinder().BindModel(new ControllerContext(), modelBinder);
        posController.ModelState.Clear();
        posController.ModelState.Merge(modelBinder.ModelState);

        ActionResult result = posController.Index(posViewModel);

        //Assert
        mockContractValidationService.Verify(m => m.Create(It.IsAny<ContractValidation>()), Times.Never);
        Assert.IsInstanceOfType(result, typeof(ViewResult));

在视图中,我正在使用不显眼的 JavaScript 验证,并且它有效。

【问题讨论】:

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


【解决方案1】:

您正在尝试同时测试两种不同的事物。控制器不负责验证模型状态,仅负责根据验证结果采取不同的行为。因此,您对控制器的单元测试不应该尝试测试验证,而应该在不同的测试中完成。在我看来,您应该进行三个单元测试:

  1. 用于验证模型验证是否正确
  2. 当模型状态有效时验证控制器是否正确运行
  3. 当模型状态无效时验证控制器行为是否正确

您可以这样做:

1.模型验证

[Test]
public void test_validation()
{
    var sut = new POSViewModel();
    // Set some properties here
    var context = new ValidationContext(sut, null, null);
    var results = new List<ValidationResult>();
    var isModelStateValid =Validator.TryValidateObject(sut, context, results, true);

    // Assert here
}

2.具有无效模型状态的控制器

[Test]
public void test_controller_with_model_error()
{
    var controller = new PosController();
    controller.ModelState.AddModelError("test", "test");

    ActionResult result = posController.Index(new PosViewModel());

    // Assert that the controller executed the right actions when the model is invalid
}

3.具有有效模型状态的控制器

[Test]
public void test_controller_with_valid_model()
{
    var controller = new PosController();
    controller.ModelState.Clear();

    ActionResult result = posController.Index(new PosViewModel());

    // Assert that the controller executed the right actions when the model is valid
}

【讨论】:

  • 感谢您的努力。但是案例 1 仍然给我 ModelState.IsValid = true。案例 2 正在运行,最好将事物分开进行测试,这样就可以了。但仍然停留在原来的问题上。
  • 我知道它有点旧的答案,但它不会用 first 单元测试来测试框架吗?因此不需要? :)
  • 不,不是真的,您将测试模型是否设置了正确的验证。是否要测试是一个不同的问题,但它绝对是在测试你的代码,而不是框架
【解决方案2】:

我找到了这个解决方案:SO: Validation does not work when I use Validator.TryValidateObject 结合@Kenneth 提供的解决方案:

[TestMethod]
    public void test_validation()
    {
        var sut = new POSViewModel();
        // Set some properties here
        var context = new ValidationContext(sut, null, null);
        var results = new List<ValidationResult>();
        TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(POSViewModel), typeof(POSViewModel)), typeof(POSViewModel));

        var isModelStateValid = Validator.TryValidateObject(sut, context, results, true);

        // Assert here
    }

如果您有一个包含所有资源的类库,请不要忘记在您的测试项目中引用它。

【讨论】:

  • 什么是断言?完成答案。
猜你喜欢
  • 2016-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多