TDD - 在 ASP.NET MVC 中测试业务规则/验证

发布于 2024-08-27 03:01:05 字数 352 浏览 7 评论 0原文

我正在使用尖锐的架构,因此我可以在单元测试和/或 TDD 期间轻松使用模拟等。我有相当复杂的业务规则,想在控制器级别测试它们。我只是想知道其他人是如何做到这一点的?

对我来说,验证在三个级别测试业务规则:

(1)属性级别(例如属性是必需的) (2) 内部资产级别(例如开始日期<结束日期) (3) 持久性级别(例如名称是唯一的,父级不能是子级的子级)

我的验证框架还将错误分配给属性。我只是想知道其他人做什么?您是否为每个业务规则编写测试并检查是否将正确的错误消息分配给正确的属性(即查看 ASP.MVC ModelState)?

我希望我的问题有意义。多谢!

最好的祝愿,

克里斯蒂安

I am using the sharp architecture so I can easily use mocks etc. in my unit tests and/or during TDD. I have quite complicated business rules and would like to test them at the controller level. I am just wondering how other people do this?

For me validation tests business rules at three levels:

(1) Property level (e.g. property is required)
(2) Intra property level (e.g. start date < end date)
(3) Persistence level (e.g. name is unique, parent cannot be child of child)

My validation framework also assigns errors to properties. I am just wondering what other people do? Do you write a test for each business rule and check whether the correct error message is assigned to the correct property (i.e. looking at the ASP.MVC ModelState)?

I hope my question makes sense. Thanks a lot!

Best wishes,

Christian

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

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

发布评论

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

评论(1

奢欲 2024-09-03 03:01:05

我通常将其分为两部分:

  1. 测试模型有效时和无效时控制器的行为是否正确。
  2. 测试模型与规则引擎的结合是否产生正确的错误消息。

基本原理是,当控制器无论是否出现错误都表现正确,并且模型本身验证正确时,应用程序就会正确运行。测试设置也将更加简单。

在最简单的帖子情况下,我们可能希望控制器在一切正常时重定向到结果页面,但在出现验证错误时重新显示相同的视图:

  [TestClass]
    public class Maintaining_todo_list
    {
        private TodoController controller;

        [TestInitialize]
        public void Setup()
        {
            controller = new TodoController {ControllerContext = new ControllerContext()};
        }

        [TestMethod]
        public void Valid_update_should_redirect_to_list()
        {
            var result = controller.Edit(1, new TodoItem {Text = "todo"});
            result.ShouldRedirectTo("list");
        }

        [TestMethod]
        public void Invalid_update_should_display_same_view()
        {
            var result = controller.Edit(1, new TodoItem {Text = ""});
            result.ShouldDisplayDefaultView();
        }
    }

模型可能会以各种方式失败并显示不同的消息:(

   [TestClass]
    public class Validating_todo_item
    {
        [TestMethod]
        public void Text_cannot_be_empty()
        {
            var todo = new TodoItem {Text = ""};
            todo.ShouldContainValidationMessage("Text cannot be empty");
        }

        [TestMethod]
        public void Text_cannot_contain_more_than_50_characters()
        {
            var todo = new TodoItem { Text = new string('a', 51) };
            todo.ShouldContainValidationMessage("Text cannot contain more than 50 characters");
        }

        [TestMethod]
        public void Valid_items()
        {
            new TodoItem { Text = new string('a', 1) }.ShouldBeValid();
            new TodoItem { Text = new string('a', 50) }.ShouldBeValid();
        }
    }

为了完整性,这是测试助手)

 internal static class AssertionHelpers
    {
        public static void ShouldRedirectTo(this ActionResult result, string action)
        {
            var redirect = result as RedirectToRouteResult;

            Assert.IsNotNull(redirect);
            Assert.AreEqual(action, redirect.RouteValues["action"]);
            Assert.IsNull(redirect.RouteValues["controller"]);
        }

        public static void ShouldDisplayDefaultView(this ActionResult result)
        {
            var view = result as ViewResult;

            Assert.IsNotNull(view);
            Assert.AreEqual("", view.ViewName);
        }

        public static void ShouldContainValidationMessage(this TodoItem todo, string message)
        {
            var context = new ValidationContext(todo, null, null);
            var results = new List<ValidationResult>();

            Validator.TryValidateObject(todo, context, results, true);

            var errors = results.Select(result => result.ErrorMessage);

            foreach (var error in errors)
            {
                Console.Out.WriteLine(error);
            }

            Assert.IsTrue(errors.Contains(message));
        }

        public static void ShouldBeValid(this TodoItem todo)
        {
            var context = new ValidationContext(todo, null, null);
            var results = new List<ValidationResult>();
            var isValid = Validator.TryValidateObject(todo, context, results, true);
            Assert.IsTrue(isValid);
        }
    }

I usually split this in two:

  1. Test if the controller behaves correctly when the model is valid and when it's not.
  2. Test if the model in combination with a rule engine produces the correct error messages.

The rationale is that when the controller behaves correctly for errors or not, and the model itself validates correctly, then the application will behave correctly. The test setup will also be simpler.

In the simplest case for a post we would probably want the controller to redirect to a result page when everything is fine, but redisplay the same view when there are validation errors:

  [TestClass]
    public class Maintaining_todo_list
    {
        private TodoController controller;

        [TestInitialize]
        public void Setup()
        {
            controller = new TodoController {ControllerContext = new ControllerContext()};
        }

        [TestMethod]
        public void Valid_update_should_redirect_to_list()
        {
            var result = controller.Edit(1, new TodoItem {Text = "todo"});
            result.ShouldRedirectTo("list");
        }

        [TestMethod]
        public void Invalid_update_should_display_same_view()
        {
            var result = controller.Edit(1, new TodoItem {Text = ""});
            result.ShouldDisplayDefaultView();
        }
    }

The model can fail in various ways with different messages:

   [TestClass]
    public class Validating_todo_item
    {
        [TestMethod]
        public void Text_cannot_be_empty()
        {
            var todo = new TodoItem {Text = ""};
            todo.ShouldContainValidationMessage("Text cannot be empty");
        }

        [TestMethod]
        public void Text_cannot_contain_more_than_50_characters()
        {
            var todo = new TodoItem { Text = new string('a', 51) };
            todo.ShouldContainValidationMessage("Text cannot contain more than 50 characters");
        }

        [TestMethod]
        public void Valid_items()
        {
            new TodoItem { Text = new string('a', 1) }.ShouldBeValid();
            new TodoItem { Text = new string('a', 50) }.ShouldBeValid();
        }
    }

(For completeness sake, here are the test helpers)

 internal static class AssertionHelpers
    {
        public static void ShouldRedirectTo(this ActionResult result, string action)
        {
            var redirect = result as RedirectToRouteResult;

            Assert.IsNotNull(redirect);
            Assert.AreEqual(action, redirect.RouteValues["action"]);
            Assert.IsNull(redirect.RouteValues["controller"]);
        }

        public static void ShouldDisplayDefaultView(this ActionResult result)
        {
            var view = result as ViewResult;

            Assert.IsNotNull(view);
            Assert.AreEqual("", view.ViewName);
        }

        public static void ShouldContainValidationMessage(this TodoItem todo, string message)
        {
            var context = new ValidationContext(todo, null, null);
            var results = new List<ValidationResult>();

            Validator.TryValidateObject(todo, context, results, true);

            var errors = results.Select(result => result.ErrorMessage);

            foreach (var error in errors)
            {
                Console.Out.WriteLine(error);
            }

            Assert.IsTrue(errors.Contains(message));
        }

        public static void ShouldBeValid(this TodoItem todo)
        {
            var context = new ValidationContext(todo, null, null);
            var results = new List<ValidationResult>();
            var isValid = Validator.TryValidateObject(todo, context, results, true);
            Assert.IsTrue(isValid);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文