In MVVM with WPF how do I unit test the link between the ViewModel and the View In MVVM with WPF how do I unit test the link between the ViewModel and the View wpf wpf

In MVVM with WPF how do I unit test the link between the ViewModel and the View


I think I've come up with something that may work using simple reflection, and adapting some code I've used in the past (the code for the FindBindingsRecursively method was written by Martin Bennedik's as part of his Enterprise WPF Validation Control):

[TestMethod]public void CheckWpfBindingsAreValid(){    // instansiate the xaml view and set DataContext    var yourView = new YourView();     yourView.DataContext = YourViewModel;    FindBindingsRecursively(yourView,            delegate(FrameworkElement element, Binding binding, DependencyProperty dp)            {                var type = yourView.DataContext.GetType();                // check that each part of binding valid via reflection                foreach (string prop in binding.Path.Path.Split('.'))                {                    PropertyInfo info = type.GetProperty(prop);                    Assert.IsNotNull(info);                    type = info.PropertyType;                }    });}private delegate void FoundBindingCallbackDelegate(FrameworkElement element, Binding binding, DependencyProperty dp);private void FindBindingsRecursively(DependencyObject element, FoundBindingCallbackDelegate callbackDelegate){    // See if we should display the errors on this element    MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Static |                BindingFlags.Public |                BindingFlags.FlattenHierarchy);    foreach (MemberInfo member in members)    {        DependencyProperty dp = null;        // Check to see if the field or property we were given is a dependency property        if (member.MemberType == MemberTypes.Field)        {            FieldInfo field = (FieldInfo)member;            if (typeof(DependencyProperty).IsAssignableFrom(field.FieldType))            {                dp = (DependencyProperty)field.GetValue(element);            }        }        else if (member.MemberType == MemberTypes.Property)        {            PropertyInfo prop = (PropertyInfo)member;            if (typeof(DependencyProperty).IsAssignableFrom(prop.PropertyType))            {                dp = (DependencyProperty)prop.GetValue(element, null);            }        }        if (dp != null)        {            // Awesome, we have a dependency property. does it have a binding? If yes, is it bound to the property we're interested in?            Binding bb = BindingOperations.GetBinding(element, dp);            if (bb != null)            {                // This element has a DependencyProperty that we know of that is bound to the property we're interested in.                 // Now we just tell the callback and the caller will handle it.                if (element is FrameworkElement)                {                    callbackDelegate((FrameworkElement)element, bb, dp);                }            }        }    }    // Now, recurse through any child elements    if (element is FrameworkElement || element is FrameworkContentElement)    {        foreach (object childElement in LogicalTreeHelper.GetChildren(element))        {            if (childElement is DependencyObject)            {                FindBindingsRecursively((DependencyObject)childElement, callbackDelegate);            }        }    }}


Really good question. Voted it up. I would like to know the answer too.

One of the best practices I know (suggested by Josh Smith, thanks Gishu for pointing to this) is having base view model class to check in the OnPropertyChanged() method whether property really exists. E.g.:

abstract class ViewModelBase{    [Conditional("DEBUG")]    public void VerifyPropertyName(string propertyName)    {        // Verify that the property name matches a real,          // public, instance property on this object.        if (TypeDescriptor.GetProperties(this)[propertyName] == null)        {            if (this.ThrowOnInvalidPropertyName)                throw new ArgumentException(propertyName);            string msg = "Invalid property name: " + propertyName;            Debug.Fail(msg);        }    }    protected void OnPropertyChanged(string propertyName)    {        VerifyPropertyName(propertyName);        PropertyChangedEventHandler handler = this.PropertyChanged;        if (handler != null)        {            var e = new PropertyChangedEventArgs(propertyName);            handler(this, e);        }    }}

But this wouldn't help you to find spelling problems in XAML. Hmm... I don't know any existing solution for this. Maybe guys from WPF Disciples could suggest something. I think I would go with PresentationTraceSources.DataBindingSource and add to his Listners collection instance of TextWriterTraceListener and then monitor output. As soon as we get an error or warning on our radar we should fail a test.

Found good example: WPF Snippet - Detecting Binding Errors

Hope this helps. At least a bit :).

Cheers, Anvaka.


I know that this is not the direct answer to your question.

If you know the name of the control element, that you expect to be bound against you can do something like the test below (using nunit). This is the rough version. But here you use expressions and explicitly test that the property is in a binding

 [Test]    public void TestBindings()    {        TestBinding<IndividualSolutionViewModel, string>(x => x.Name, "Name", TextBlock.TextProperty);    }    private void TestBinding<TViewModel,TResult>(Expression<Func<TViewModel, TResult>> property, string elementName,         DependencyProperty dependencyProperty)    {        string memberName = ExpressionHelper.GetPropertyName(property); // f.ex v => v.Name will return Name        TestBinding(memberName, elementName, dependencyProperty);    }    private void TestBinding(string memberName, string elementInControlName, DependencyProperty dependencyProperty)    {        //object viewModel = new IndividualSolutionViewModel();        var view = new IndividualSolutionView();        //Assert.That(view.DataContext, Is.EqualTo(viewModel));        var element = view.FindName(elementInControlName);        Assert.That(element, Is.Not.Null, string.Format("Unable to find the element {0} in view {1}", elementInControlName, view.Name));        Assert.That(element, Is.InstanceOf(typeof(DependencyObject)));        var binding = BindingOperations.GetBinding(element as DependencyObject, dependencyProperty);        Assert.That(binding, Is.Not.Null, string.Format("Could not find a binding for the control {0}", elementInControlName));        Assert.That(binding.Path.Path, Is.EqualTo(memberName));    }

Ps. You have to add this to the app.config

<configSections>    <sectionGroup name="NUnit">        <section type="System.Configuration.NameValueSectionHandler"                 name="TestRunner"></section>    </sectionGroup></configSections><NUnit>    <TestRunner>        <add value="STA" key="ApartmentState"></add>    </TestRunner></NUnit>