Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test WPF Bindings

I am trying to unit test my WPF databindings using the test suit provided by Microsoft Team System. I would like to be able to test the bindings without showing the window because most of my tests will be for user controls and not actually on a window. Is this possible or is there a better way to do it? The code below works if I show the window, but if I don't, the bindings don't update.

            Window1_Accessor target = new Window1_Accessor();
            UnitTestingWPF.Window1_Accessor.Person p = new UnitTestingWPF.Window1_Accessor.Person() { FirstName = "Shane" };
            Window1 window = (target.Target as Window1);
            window.DataContext = p;         
            //window.Show(); //Only Works when I actually show the window
            //Is it possible to manually update the binding here, maybe?  Is there a better way?
            Assert.AreEqual("Shane", target.textBoxFirstName.Text);  //Fails if I don't Show() the window because the bindings aren't updated
like image 743
NotDan Avatar asked Dec 01 '08 15:12

NotDan


3 Answers

Eyeball it.
This kind of declarative markup rarely breaks.. unless someone goes in manual and screws it up. Even then, you can fix it within minutes. IMHO the cost of writing such tests far outweigh the benefits.

Update[Dec3,08]: Alrighty then.
The test is just testing that the textbox has the value "FirstName" as the Path property of the binding. If I change/refactor FirstName to JustName in the actual data source object, the test would still pass since it is testing against an anonymous type. (Green test when code broken - TDD Antipattern: The Liar) If your aim is to verify that FirstName has been specified in XAML,

Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);

If you really must catch broken bindings via unit tests (and don't want to show the UI), use the real data source... struggled for a while and came up with this.

[Test]
public void TestTextBoxBinding()
{
   MyWindow w = new MyWindow();
   TextBox txtBoxToProbe = w.TextBox1;
   Object obDataSource = w;               // use 'real' data source 

   BindingExpression bindingExpr = BindingOperations.GetBindingExpression(txtBoxToProbe, TextBox.TextProperty);
   Binding newBind = new Binding(bindingExpr.ParentBinding.Path.Path);
   newBind.Source = obDataSource;
   txtBoxToProbe.SetBinding(TextBox.TextProperty, newBind);

   Assert.AreEqual("Go ahead. Change my value.", txtBoxToProbe.Text);
} 

Epilogue: There's some real covert stuff happening in the call to Window.Show(). It somehow magically sets up the DataItem property after which data binding starts working.

// before show
bindingExpr.DataItem => null
bindingExpr.Status => BindingStatus.Unattached

// after show
bindingExpr.DataItem => {Actual Data Source}
bindingExpr.Status => BindingStatus.Active

Once the Binding is Active, I guess you can force textbox updates via code like this..

txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

Once again, I voice my reluctance against this approach. Getting NUnit to run in STA was a pain..

like image 132
Gishu Avatar answered Sep 19 '22 18:09

Gishu


While looking for a solution to convert WPF binding errors into exception, I figured out that it can also be used in a unit test project.

The technique is very simple:

  1. Derive a TraceListener that throws instead of logging
  2. Add that listener to PresentationTraceSources.DataBindingSource

Please see the complete solution on GitHub, it includes a unit test project.

Failed test in Visual Studio

like image 7
Benoit Blanchon Avatar answered Oct 17 '22 16:10

Benoit Blanchon


Shane, if what you're really worried about is a binding breaking silently, you should look at redirecting the binding traces to somewhere you can examine. I'd start here:

http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx

Other than that, I agree with Gishu that bindings aren't good candidates for unit testing, mainly due to the automagic going on that Gishu mentioned in the "Epilogue". Instead focus on making sure the underlying class behaves correctly.

Note, too, that you can get even more robust traces using the PresentationTraceSources class:

http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx

Hope that helps!

like image 2
Bob King Avatar answered Oct 17 '22 18:10

Bob King