views:

1072

answers:

4

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
+1  A: 

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..

Gishu
If we are binding to properties in a class, and refactor the class, the xaml will still compile but no exception will be thrown and our app will not longer function correctly as the bindings will be incorrect. This has been an issue for us already which is why we are looking for a solution.
NotDan
+3  A: 

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!

Bob King
A: 

I believe that this activity belongs in an integration test not UnitTests - ie an automated or real user running tests that show dialogs and confirm that the UI is populated or not.

Writing UnitTests for this sort of UI concern is time consuming and I'm not convinced it's worth the effort - you get not much for a lot of work.

That's not to say I wouldn't test it. I would invest the time in creating automated integration tests (eg using White)

PandaWood