Unit Tests and Debug.Assert()
I recently found some code that had a couple of issues:
- There was a try/catch block that did a "catch(Exception e)". And no, it didn't rethrow the exception. See item #2.
- Inside the exception handler, it had the following code:
catch (Exception e)
{
Debug.WriteLine(e);
Debug.Assert(false);
}
I thought long and hard on why anyone would do such a thing and I think I know why. Now, this is purely conjecture, but it does explain things. This code was found inside a custom PropertyDescriptor – namely, the GetValue and SetValue methods both had this code. I suspect that the Debug.Assert() was placed inside the catch block of these two methods so that no consumers of this component had to worry about possible data binding errors. If there ever was a problem with data binding, the Debug.Assert(false) would throw up a dialog with a stack trace and some "Abort/Retry/Ignore" buttons, the user would click "Retry" and re-edit their data. I don't like it, but since this component is used in a number of places in a data binding scenario, that's my theory.
The problem for me is that I needed to fix some bugs in this custom PropertyDescriptor and this code had no unit tests. So before I could consider modifying anything, I had to get the current behavior under test. Obviously, I had to remove the Debug.Assert(), but, for legacy code, I still had to have that (ugly!) behavior.
Since the Debug.Assert() is being used to report errors, I started by defining an interface that would be used for reporting errors:
public interface IPropertyDescriptorValueErrorReporting
{
void SetValueError(Exception e);
void GetValueError(Exception e);
}
I then moved the existing error handling code into a class I called "DefaultPropertyDescriptorValueErrorReporting":
public class DefaultPropertyDescriptorValueErrorReporting : IPropertyDescriptorValueErrorReporting
{
#region IPropertyDescriptorValueErrorReporting Members
public void SetValueError(Exception e)
{
Debug.WriteLine(e);
Debug.Assert(false);
}
public void GetValueError(Exception e)
{
Debug.WriteLine(e);
Debug.Assert(false);
}
#endregion
}
I then added an overload on the constructor of this custom property descriptor to accept an IPropertyDescriptorValueErrorReporting
public CustomPropertyDescriptor(string name, Type type, int index, IPropertyDescriptorValueErrorReporting errorReporting)
: base(name, null)
For legacy code, the old constructor is modified to use the "DefaultPropertyDescriptorValueErrorReporting":
public CustomPropertyDescriptor(string name, Type type, int index)
: this(name, type, index, new DefaultPropertyDescriptorValueErrorReporting())
Finally, we change the exception handler to call our interface methods:
catch (Exception e)
{
errorReporting.GetValueError(e);
}
And we do a call to SetValueError in the SetValue exception handler.
Now I'm done! I can proceed to create unit tests and I can pass in a mocked IPropertyDescriptorValueErrorReporting and set expectations that the GetValueError and SetValueError methods are called at the appropriate times. Plus, I've also created an extension point so other applications can hook in to the error reporting and decide how to handle errors on a case-by-case basis.