如何对 IDataErrorInfo 业务对象中的错误进行单元测试?

发布于 2024-08-22 16:44:40 字数 1712 浏览 5 评论 0原文

我正在为 WPF 应用程序编写(尝试编写)单元测试。

UI 绑定以实现 IDataErrorInfo 的业务对象,这样当我在 View xaml 中设置 ValidatesOnDataErrors=True 时,只要调用绑定业务对象的 setter,就会调用错误索引器 (this[])。 那部分很棒。

现在,如果我从 unitTest 调用同一属性的 setter,它永远不会调用错误索引器。如何强制从单元测试中评估 IDataErrorInfo 索引器?

仅出于说明目的,这是我的简单错误索引器之一,其中包含 Name 属性。设置 'myObject.Name = string.Empty;'当我在单元测试中执行此操作时,确实会调用设置器,但不会调用错误索引器。

        public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            IsDirty = true;
            OnPropertyChanged("Name");
        }
    }

        #region IDataErrorInfo

    public Dictionary<string, string> ErrorCollection;

    public string this[string property]
    {
        get
        {
            string msg = null;
            switch (property)
            {
                case "Name":
                    if (string.IsNullOrEmpty(Name))
                        msg = "ICU Name is required.";
                    else if (Name.Length < 4)
                        msg = "ICU Name must contain at least 4 characters.";
                    else if (_parent.Units.AsEnumerable().Count(u => u.Name == Name) > 1)
                        msg = Name + " already exists, please change to a different Name.";
                    break;
            }


            if (msg != null && !ErrorCollection.ContainsKey(property))
                ErrorCollection.Add(property, msg);
            if (msg == null && ErrorCollection.ContainsKey(property))
                ErrorCollection.Remove(property);

            return msg;
        }
    }

    public string Error
    {
        get { return null; }
    }
    #endregion

谢谢!

I am writing (attempting to write) unit tests for a WPF application.

The business objects that the UI binds to implement IDataErrorInfo, such that when I set ValidatesOnDataErrors=True in my View xaml, the error indexer (this[]) is called anytime the setter for the bound business object is called.
That part is great.

Now if I call that same property's setter from a unitTest, it never calls the error indexer. How do I force the IDataErrorInfo indexer to be evaluated from within a unit test?

just for illustration, here is one of my simple error indexers that contains a Name property. Setting 'myObject.Name = string.Empty;' does call the setter, but not the error indexer when I do this in my unit tests.

        public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            IsDirty = true;
            OnPropertyChanged("Name");
        }
    }

        #region IDataErrorInfo

    public Dictionary<string, string> ErrorCollection;

    public string this[string property]
    {
        get
        {
            string msg = null;
            switch (property)
            {
                case "Name":
                    if (string.IsNullOrEmpty(Name))
                        msg = "ICU Name is required.";
                    else if (Name.Length < 4)
                        msg = "ICU Name must contain at least 4 characters.";
                    else if (_parent.Units.AsEnumerable().Count(u => u.Name == Name) > 1)
                        msg = Name + " already exists, please change to a different Name.";
                    break;
            }


            if (msg != null && !ErrorCollection.ContainsKey(property))
                ErrorCollection.Add(property, msg);
            if (msg == null && ErrorCollection.ContainsKey(property))
                ErrorCollection.Remove(property);

            return msg;
        }
    }

    public string Error
    {
        get { return null; }
    }
    #endregion

Thanks!

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

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

发布评论

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

评论(3

自在安然 2024-08-29 16:44:40

您需要自己挂钩 PropertyChanged 事件,然后在调用处理程序时,使用属性名称调用索引器。或者不要挂钩事件并使用您正在测试的属性的名称调用索引器。

这就是 .NET 所做的事情。它使用属性名称调用索引器。

MyClass mc = new MyClass();
mc.Name = "abc";
string error = mc["Name"];

You need to hook into the PropertyChanged event yourself and then when your handler is called, call the indexer with the property name. Or don't hook into the event and call the indexer with the name of the property you're testing.

That's what .NET does. It calls the indexer with property names.

MyClass mc = new MyClass();
mc.Name = "abc";
string error = mc["Name"];
牵你手 2024-08-29 16:44:40

我在单元测试中要做的是设置 Name 的值,然后显式调用索引器来检查错误。

您可以在单元测试中挂钩 PropertyChanged 事件,但我认为这没有意义,因为您必须以某种方式通知测试方法已收到该事件,然后您必须调用索引器。

What I would do in a unit test is to set the value of Name, then explicitly call the indexer to check for the error.

You could hook the PropertyChanged event in the unit test, but I don't think it makes sense, as you'd then have to somehow notify the test method that the event was received and then you'd have to call the indexer anyway.

起风了 2024-08-29 16:44:40

以下 NUnit 测试调用 Name 属性的索引器并验证错误消息。

[TestFixture]
public class MyClassTests
{
    [TestCase("", "ICU Name is required.")]
    [TestCase("A Valid Name", null)]
    public void ValidationIcuNameIsRequired(string name, string expectedResult)
    {
        // Arrange 
        var systemUnderTest = new MyClass();

        // Act
        systemUnderTest.Name = name;

        // Assert
        Assert.AreEqual(expectedResult, systemUnderTest[nameof(systemUnderTest.Name)]);
    }

    [TestCase("a", "ICU Name must contain at least 4 characters.")]
    [TestCase("ab", "ICU Name must contain at least 4 characters.")]
    [TestCase("abc", "ICU Name must contain at least 4 characters.")]
    [TestCase("abcd", null)]
    [TestCase("abcde", null)]
    public void ValidationIcuNameLongerThanThreeCharacters(string name, string expectedResult)
    {
        // Arrange 
        var systemUnderTest = new MyClass();

        // Act
        systemUnderTest.Name = name;

        // Assert
        Assert.AreEqual(expectedResult, systemUnderTest[nameof(systemUnderTest.Name)]);
    }

    [Test]
    public void ValidationParentDoesNotExist()
    {
        // Arrange
        var systemUnderTest = new MyClass();
        string icuName = "TestName";            
        systemUnderTest.Parent.Units.Add(new Unit() {Name = icuName });

        // Act
        systemUnderTest.Name = icuName;

        // Assert
        Assert.AreEqual(icuName + " already exists, please change to a different Name.", systemUnderTest[nameof(systemUnderTest.Name)]);
    }
}

The following NUnit tests called the indexer for the Name property and validate the Error message.

[TestFixture]
public class MyClassTests
{
    [TestCase("", "ICU Name is required.")]
    [TestCase("A Valid Name", null)]
    public void ValidationIcuNameIsRequired(string name, string expectedResult)
    {
        // Arrange 
        var systemUnderTest = new MyClass();

        // Act
        systemUnderTest.Name = name;

        // Assert
        Assert.AreEqual(expectedResult, systemUnderTest[nameof(systemUnderTest.Name)]);
    }

    [TestCase("a", "ICU Name must contain at least 4 characters.")]
    [TestCase("ab", "ICU Name must contain at least 4 characters.")]
    [TestCase("abc", "ICU Name must contain at least 4 characters.")]
    [TestCase("abcd", null)]
    [TestCase("abcde", null)]
    public void ValidationIcuNameLongerThanThreeCharacters(string name, string expectedResult)
    {
        // Arrange 
        var systemUnderTest = new MyClass();

        // Act
        systemUnderTest.Name = name;

        // Assert
        Assert.AreEqual(expectedResult, systemUnderTest[nameof(systemUnderTest.Name)]);
    }

    [Test]
    public void ValidationParentDoesNotExist()
    {
        // Arrange
        var systemUnderTest = new MyClass();
        string icuName = "TestName";            
        systemUnderTest.Parent.Units.Add(new Unit() {Name = icuName });

        // Act
        systemUnderTest.Name = icuName;

        // Assert
        Assert.AreEqual(icuName + " already exists, please change to a different Name.", systemUnderTest[nameof(systemUnderTest.Name)]);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文