xVal 在检查 ModelState.IsValid 时导致 NUnit 崩溃

发布于 2024-08-22 00:31:45 字数 3122 浏览 6 评论 0原文

我正在尝试在 MVC 中的控制器上运行一个简单的测试。该控制器使用 xVal 和 DataAnnotations 验证输入。当我运行测试(通过 Resharper 使用 NUnit、NUnit 独立版或 TestDriven.Net)时,它会导致运行器崩溃,并且没有正确的错误消息。在事件日志中,它只有一条相当通用的 .NET Runtime 2.0 错误报告消息,表明运行程序是一个出现故障的应用程序。

该错误是由对 ModelState.IsValid 的调用引起的(我知道这一点是因为当我将其取出时它运行良好)。另外,只有当我正常运行测试时才会发生崩溃。当我在调试模式下运行测试时,它工作正常。

当我删除对 xVal 的引用并使用 ModelState.AddModelError 在模型状态上设置错误时,它不会崩溃。

下面是被测控制器和测试类。你能看出这里有什么不对劲吗?

被测控制器

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using xVal.ServerSide;

namespace TestModelState.Controllers
{
    public class ThingController : Controller
    {
        [HttpPost]
        public ActionResult Create(Thing thing)
        {
            try
            {
                var errors = DataAnnotationsValidationRunner.GetErrors(thing);
                if (errors.Any())
                {
                    throw new RulesException(errors);
                }
            }
            catch (RulesException ex)
            {
                ex.AddModelStateErrors(ModelState, "thing");
            }
            if (ModelState.IsValid)
            {
                // Do some save stuff here
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
    }

    public class Thing
    {
        [Required]
        public string Name { get; set; }
    }

    internal static class DataAnnotationsValidationRunner
    {
        public static IEnumerable<ErrorInfo> GetErrors(object instance)
        {
            return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
                   from attribute in prop.Attributes.OfType<ValidationAttribute>()
                   where !attribute.IsValid(prop.GetValue(instance))
                   select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
        }
    }
}

测试类

using System.Web.Mvc;
using NUnit.Framework;
using TestModelState.Controllers;

namespace TestModelState.Tests
{
    [TestFixture]
    public class ThingControllerTests
    {
        [Test]
        public void Create_InvalidThing_SetsModelState()
        {
            // Arrange
            var thingController = new ThingController();
            var thing = new Thing();

            // Act
            var result = thingController.Create(thing);

            // Assert
            var viewResult = (ViewResult)result;
            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
        }
    }
}

版本 - ASP.Net MVC - 2.0.0.0、NUnit - 2.5.3.9345、xVal - 1.0.0.0

更新 当我使用以下语句时(这就是 ModelState.IsValid 在幕后所做的事情),崩溃不会发生...

var modelStateIsValid = ModelState.Values.All(ms => ms.Errors.Count == 0);

我仍然更喜欢使用 ModelState.IsValid,但至少这是一种解决方法。

I am trying to run a simple test on a controller in MVC. This controller validates the input using xVal and DataAnnotations. When I run the test (using NUnit via Resharper, NUnit standalone or TestDriven.Net) it crashes the runner with no decent error message. In the event logs it just has a fairly generic .NET Runtime 2.0 Error Reporting message saying that the runner was a Faulting application.

The error is caused by a call to ModelState.IsValid (I know this because when I take it out it runs fine). Also, the crash only occurs when I run the test normally. When I run the test in debug mode it works fine.

When I remove the references to xVal and set an error on the modelstate using ModelState.AddModelError, it doesn't crash.

Below is the controller under test and the test class. Can you see anything amiss here?

Controller Under Test

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using xVal.ServerSide;

namespace TestModelState.Controllers
{
    public class ThingController : Controller
    {
        [HttpPost]
        public ActionResult Create(Thing thing)
        {
            try
            {
                var errors = DataAnnotationsValidationRunner.GetErrors(thing);
                if (errors.Any())
                {
                    throw new RulesException(errors);
                }
            }
            catch (RulesException ex)
            {
                ex.AddModelStateErrors(ModelState, "thing");
            }
            if (ModelState.IsValid)
            {
                // Do some save stuff here
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
    }

    public class Thing
    {
        [Required]
        public string Name { get; set; }
    }

    internal static class DataAnnotationsValidationRunner
    {
        public static IEnumerable<ErrorInfo> GetErrors(object instance)
        {
            return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
                   from attribute in prop.Attributes.OfType<ValidationAttribute>()
                   where !attribute.IsValid(prop.GetValue(instance))
                   select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
        }
    }
}

Test Class

using System.Web.Mvc;
using NUnit.Framework;
using TestModelState.Controllers;

namespace TestModelState.Tests
{
    [TestFixture]
    public class ThingControllerTests
    {
        [Test]
        public void Create_InvalidThing_SetsModelState()
        {
            // Arrange
            var thingController = new ThingController();
            var thing = new Thing();

            // Act
            var result = thingController.Create(thing);

            // Assert
            var viewResult = (ViewResult)result;
            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
        }
    }
}

Versions - ASP.Net MVC - 2.0.0.0, NUnit - 2.5.3.9345, xVal - 1.0.0.0

Update
When I use the following statement instead (which is what ModelState.IsValid is doing under the covers) the crash does not occur...

var modelStateIsValid = ModelState.Values.All(ms => ms.Errors.Count == 0);

I'd still prefer to use ModelState.IsValid, but at least this is a workaround.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

他夏了夏天 2024-08-29 00:31:46

ModelState.IsValid 不是由控制器设置的,而是由模型绑定框架设置的。模型绑定框架仅在处理传入的 HTTP 请求时才会触发。当您像上面那样显式调用控制器操作时,模型绑定不会发生,因此整个 ModelState 处于不确定状态。你有几种方法

  1. 模仿这个SO问题中给出的测试中的整个模型绑定内容 - 我如何测试 ModelState?

  2. 您可以在控制器操作中使用 TryUpdateModel 方法,如此问题 - MVC 验证的单元测试

  3. 编写单独的单元测试来测试模型的基于属性的验证。为此,您需要更改基于属性的验证方法。阅读本文了解详细信息 - http://jesschadwick。 blogspot.com/2009/04/cleaner-validation-with-aspnet-mvc.html

The ModelState.IsValid is not set by the controller, it is set by the model binding framework. Model binding is framework is fired only when processing the incoming HTTP requests. When you call the controller actions explicitly like above, the model binding does not happen and hence whole ModelState is in an indeterminate state. you have got few ways

  1. Mimic the whole model binding stuff in your tests as given in this SO question - How can I test ModelState?

  2. You can use TryUpdateModel method in your controller action as given in this SO question - Unit tests on MVC validation

  3. Write separate unit tests that test for the attribute based validations for your models. For this you would need to change your approach towards attributes based validation. Read this article for details - http://jesschadwick.blogspot.com/2009/04/cleaner-validation-with-aspnet-mvc.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文