How do I unit test errors in an IDataErrorInfo business object?
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 = Na开发者_如何学运维me + " 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!
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"];
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.
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)]);
}
}
精彩评论