2008-12-01 119 views
10

我想单元测试我的WPF数据绑定,使用Microsoft Team System提供的测试套装。我希望能够在不显示窗口的情况下测试绑定,因为我的大多数测试都是针对用户控件,而不是实际上在窗口上。这是可能的还是有更好的方法来做到这一点?下面的代码工作,如果我显示窗口,但如果我不这样做,绑定不会更新。单元测试WPF绑定

  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 

回答

2

Shane,如果你真的担心的是一个无声的破坏绑定,你应该看看重定向绑定痕迹到你可以检查的地方。我会从这里开始:

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

其他,我有Gishu同意绑定,则不适合进行单元测试,主要是由于AUTOMAGIC正在进行中的“后记”中提到Gishu。而是专注于确保基础类的行为正确。

注意,那就是,你可以得到更强大的痕迹使用PresentationTraceSources类:

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

希望帮助!

1

眼球吧。
这种声明性标记很少打破..除非有人进入手动并将其拧紧。即使这样,你可以在几分钟之内解决它。恕我直言,编写这些测试的成本远远超过了好处。

更新 [Dec3,08]:那好吧。
测试仅测试文本框的值为“FirstName”作为绑定的Path属性。如果我在实际的数据源对象中将FirstName更改/重构为JustName,那么测试仍然会通过,因为它正在针对匿名类型进行测试。 (绿测试时,代码破 - TDD反模式:说谎者) 如果你的目的是验证名字已经在XAML被指定,

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

如果你真的必须赶通过单元测试打破绑定(以及不想要展示用户界面),使用真实的数据源...挣扎了一段时间,并想出了这一点。

[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); 
} 

结语: 有一些real covert stuff发生在调用Window.Show()。它奇迹般地设置DataItem属性,之后数据绑定开始工作。

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

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

一旦绑定活动,我想你可以通过代码迫使这样的文本框更新..

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

,我再次表达我对这种做法不情愿。让NUnit在STA上运行是件痛苦的事情。

+2

如果我们在类结合的特性,并重构类,XAML中仍然会编译,但没有将引发异常,我们的应用程序将无法再正常的绑定将是不正确的。这对我们来说是一个问题,这就是我们寻找解决方案的原因。 – NotDan 2008-12-02 15:48:23

0

你可以试试Guia。 有了它,您可以对您的UserControl进行单元测试,并检查数据绑定是否正确。你必须显示窗口。

这里是一个例子。它启动一个新的UserControl实例并设置其DataContext,然后检查文本框是否设置为正确的值。

[TestMethod] 
    public void SimpleTest() 
    { 
     var viewModel = new SimpleControlViewModel() {TextBoxText = "Some Text"}; 

     customControl = CustomControl.Start<SimpleUserControl>((control) => control.DataContext = viewModel); 

     Assert.AreEqual("Some Text", customControl.Get<TextBox>("textbox1").Value); 

     customControl.Stop(); 
    } 
1

结合的意见,我在多个岗位SO我写了下面的类,它工作得非常好测试WPF绑定碰到。

public static class WpfBindingTester 
{ 
    /// <summary>load a view in a hidden window and monitor it for binding errors</summary> 
    /// <param name="view">a data-bound view to load and monitor for binding errors</param> 
    public static void AssertBindings(object view) 
    { 
     using (InternalTraceListener listener = new InternalTraceListener()) 
     { 
      ManualResetEventSlim mre = new ManualResetEventSlim(false); 

      Window window = new Window 
      { 
       Width = 0, 
       Height = 0, 
       WindowStyle = WindowStyle.None, 
       ShowInTaskbar = false, 
       ShowActivated = false, 
       Content = view 
      }; 

      window.Loaded += (_, __) => mre.Set(); 
      window.Show(); 

      mre.Wait(); 

      window.Close(); 

      Assert.That(listener.ErrorMessages, Is.Empty, listener.ErrorMessages); 
     } 
    } 

    /// <summary>Is the test running in an interactive session. Use with Assume.That(WpfBindingTester.IsAvailable) to make sure tests only run where they're able to</summary> 
    public static bool IsAvailable { get { return Environment.UserInteractive && Process.GetCurrentProcess().SessionId != 0; } } 


    private class InternalTraceListener : TraceListener 
    { 
     private readonly StringBuilder _errors = new StringBuilder(); 
     private readonly SourceLevels _originalLevel; 
     public string ErrorMessages { get { return _errors.ToString(); } } 

     static InternalTraceListener() { PresentationTraceSources.Refresh(); } 

     public InternalTraceListener() 
     { 
      _originalLevel = PresentationTraceSources.DataBindingSource.Switch.Level; 
      PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error; 
      PresentationTraceSources.DataBindingSource.Listeners.Add(this); 
     } 

     public override void Write(string message) {} 

     public override void WriteLine(string message) { _errors.AppendLine(message); } 

     protected override void Dispose(bool disposing) 
     { 
      PresentationTraceSources.DataBindingSource.Listeners.Remove(this); 
      PresentationTraceSources.DataBindingSource.Switch.Level = _originalLevel; 
      base.Dispose(disposing); 
     } 
    } 
} 
4

在寻找将WPF绑定错误转换为异常的解决方案时,我发现它也可以在单元测试项目中使用。

该技术是非常简单的:

  1. 派生一个TraceListener抛出,而不是记录
  2. 该侦听器添加到PresentationTraceSources.DataBindingSource

请参阅complete solution on GitHub,它包括一个单元测试项目。

Failed test in Visual Studio