Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

In MVVM it is normal to connect View to the ViewModel with data binding.

Therefore if the name of a properties changes on one of the Model objects that is databound to there is no compiler error.

When the compiler will not stop a bug, the next thing I think of is “UnitTest”, However

How do you unit test this without spending forever writing a GUI test?

Is there a system that will check that all the properties that are bound to is valid, (without having to run the UI) that I can call in a unit test?

I am looking for something that will take the view, and then loop over all WPF controls, for each WPF control it will look at all the binding and check if they are valid.


By the way there have been a few good questions about how to make OnPropertyChanged safe, and/or how to test it (But done of these get down to the level of a WPF view.)

  • How to make Databinding type safe and support refactoring
  • Automatically INotifyPropertyChanged
  • workarounds for nameof() operator in C#: typesafe databinding
  • A Fluent Interface For Testing INotifyPropertyChanged
  • Automatic Class Tester will test all simple proptites and INotifyPropertyChanged

I have put a bounty on this question, as someone must have thought hard about the problem and come up with soltions.

like image 286
Ian Ringrose Avatar asked Feb 18 '10 13:02

Ian Ringrose


People also ask

How do I link views and ViewModel?

The other approach to bind the View and Viewmodel in View First Approach is in the xaml itself as shown in the figure below. I am setting the datacontext in the . xaml of the View itself. To set the datacontext in this way the ViewModel class need to have a default constructor.

How do you write unit testing for MVVM?

Setup Unit Testing Dependencies In your Android Studio Project, the following are the three important packages inside the src folder: app/src/main/java/ — Main java source code folder. app/src/test/java/ — Local unit test folder. app/src/androidTest/java/ — Instrumentation test folder.

How and what we will test in MVVM?

MVVM – Unit Testing The idea behind unit testing is to take discrete chunks of code (units) and write test methods that use the code in an expected way, and then test to see if they get the expected results. Being code themselves, unit tests are compiled just like the rest of the project.


2 Answers

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);
            }
        }
    }
}
like image 126
Bermo Avatar answered Oct 12 '22 22:10

Bermo


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.

like image 33
Anvaka Avatar answered Oct 12 '22 22:10

Anvaka